more about blank removal
[roojs1] / roojs-bootstrap-debug.js
1 Roo.bootstrap = {};/**
2  * set the version of bootstrap based on the stylesheet...
3  *
4  */
5
6 Roo.bootstrap.version = ( function() {
7     var ret=3;
8     Roo.each(document.styleSheets, function(s) {
9         if ( s.href  && s.href.match(/css-bootstrap4/)) {
10             ret=4;
11         }
12     });
13     if (ret > 3) {
14          Roo.Element.prototype.visibilityMode = Roo.Element.DISPLAY;
15     }
16     return ret;
17 })(); Roo.bootstrap.menu = Roo.bootstrap.menu || {};
18 Roo.bootstrap.nav = {};
19
20 Roo.bootstrap.form = {};Roo.bootstrap.panel = {};Roo.bootstrap.layout = {};
21 Roo.htmleditor = {};
22 Roo.namespace('Roo.bootstrap.form.HtmlEditorToolbar');
23 /*
24  * Based on:
25  * Ext JS Library 1.1.1
26  * Copyright(c) 2006-2007, Ext JS, LLC.
27  *
28  * Originally Released Under LGPL - original licence link has changed is not relivant.
29  *
30  * Fork - LGPL
31  * <script type="text/javascript">
32  */
33
34
35 /**
36  * @class Roo.Shadow
37  * Simple class that can provide a shadow effect for any element.  Note that the element MUST be absolutely positioned,
38  * and the shadow does not provide any shimming.  This should be used only in simple cases -- for more advanced
39  * functionality that can also provide the same shadow effect, see the {@link Roo.Layer} class.
40  * @constructor
41  * Create a new Shadow
42  * @param {Object} config The config object
43  */
44 Roo.Shadow = function(config){
45     Roo.apply(this, config);
46     if(typeof this.mode != "string"){
47         this.mode = this.defaultMode;
48     }
49     var o = this.offset, a = {h: 0};
50     var rad = Math.floor(this.offset/2);
51     switch(this.mode.toLowerCase()){ // all this hideous nonsense calculates the various offsets for shadows
52         case "drop":
53             a.w = 0;
54             a.l = a.t = o;
55             a.t -= 1;
56             if(Roo.isIE){
57                 a.l -= this.offset + rad;
58                 a.t -= this.offset + rad;
59                 a.w -= rad;
60                 a.h -= rad;
61                 a.t += 1;
62             }
63         break;
64         case "sides":
65             a.w = (o*2);
66             a.l = -o;
67             a.t = o-1;
68             if(Roo.isIE){
69                 a.l -= (this.offset - rad);
70                 a.t -= this.offset + rad;
71                 a.l += 1;
72                 a.w -= (this.offset - rad)*2;
73                 a.w -= rad + 1;
74                 a.h -= 1;
75             }
76         break;
77         case "frame":
78             a.w = a.h = (o*2);
79             a.l = a.t = -o;
80             a.t += 1;
81             a.h -= 2;
82             if(Roo.isIE){
83                 a.l -= (this.offset - rad);
84                 a.t -= (this.offset - rad);
85                 a.l += 1;
86                 a.w -= (this.offset + rad + 1);
87                 a.h -= (this.offset + rad);
88                 a.h += 1;
89             }
90         break;
91     };
92
93     this.adjusts = a;
94 };
95
96 Roo.Shadow.prototype = {
97     /**
98      * @cfg {String} mode
99      * The shadow display mode.  Supports the following options:<br />
100      * sides: Shadow displays on both sides and bottom only<br />
101      * frame: Shadow displays equally on all four sides<br />
102      * drop: Traditional bottom-right drop shadow (default)
103      */
104     mode: false,
105     /**
106      * @cfg {String} offset
107      * The number of pixels to offset the shadow from the element (defaults to 4)
108      */
109     offset: 4,
110
111     // private
112     defaultMode: "drop",
113
114     /**
115      * Displays the shadow under the target element
116      * @param {String/HTMLElement/Element} targetEl The id or element under which the shadow should display
117      */
118     show : function(target){
119         target = Roo.get(target);
120         if(!this.el){
121             this.el = Roo.Shadow.Pool.pull();
122             if(this.el.dom.nextSibling != target.dom){
123                 this.el.insertBefore(target);
124             }
125         }
126         this.el.setStyle("z-index", this.zIndex || parseInt(target.getStyle("z-index"), 10)-1);
127         if(Roo.isIE){
128             this.el.dom.style.filter="progid:DXImageTransform.Microsoft.alpha(opacity=50) progid:DXImageTransform.Microsoft.Blur(pixelradius="+(this.offset)+")";
129         }
130         this.realign(
131             target.getLeft(true),
132             target.getTop(true),
133             target.getWidth(),
134             target.getHeight()
135         );
136         this.el.dom.style.display = "block";
137     },
138
139     /**
140      * Returns true if the shadow is visible, else false
141      */
142     isVisible : function(){
143         return this.el ? true : false;  
144     },
145
146     /**
147      * Direct alignment when values are already available. Show must be called at least once before
148      * calling this method to ensure it is initialized.
149      * @param {Number} left The target element left position
150      * @param {Number} top The target element top position
151      * @param {Number} width The target element width
152      * @param {Number} height The target element height
153      */
154     realign : function(l, t, w, h){
155         if(!this.el){
156             return;
157         }
158         var a = this.adjusts, d = this.el.dom, s = d.style;
159         var iea = 0;
160         s.left = (l+a.l)+"px";
161         s.top = (t+a.t)+"px";
162         var sw = (w+a.w), sh = (h+a.h), sws = sw +"px", shs = sh + "px";
163  
164         if(s.width != sws || s.height != shs){
165             s.width = sws;
166             s.height = shs;
167             if(!Roo.isIE){
168                 var cn = d.childNodes;
169                 var sww = Math.max(0, (sw-12))+"px";
170                 cn[0].childNodes[1].style.width = sww;
171                 cn[1].childNodes[1].style.width = sww;
172                 cn[2].childNodes[1].style.width = sww;
173                 cn[1].style.height = Math.max(0, (sh-12))+"px";
174             }
175         }
176     },
177
178     /**
179      * Hides this shadow
180      */
181     hide : function(){
182         if(this.el){
183             this.el.dom.style.display = "none";
184             Roo.Shadow.Pool.push(this.el);
185             delete this.el;
186         }
187     },
188
189     /**
190      * Adjust the z-index of this shadow
191      * @param {Number} zindex The new z-index
192      */
193     setZIndex : function(z){
194         this.zIndex = z;
195         if(this.el){
196             this.el.setStyle("z-index", z);
197         }
198     }
199 };
200
201 // Private utility class that manages the internal Shadow cache
202 Roo.Shadow.Pool = function(){
203     var p = [];
204     var markup = Roo.isIE ?
205                  '<div class="x-ie-shadow"></div>' :
206                  '<div class="x-shadow"><div class="xst"><div class="xstl"></div><div class="xstc"></div><div class="xstr"></div></div><div class="xsc"><div class="xsml"></div><div class="xsmc"></div><div class="xsmr"></div></div><div class="xsb"><div class="xsbl"></div><div class="xsbc"></div><div class="xsbr"></div></div></div>';
207     return {
208         pull : function(){
209             var sh = p.shift();
210             if(!sh){
211                 sh = Roo.get(Roo.DomHelper.insertHtml("beforeBegin", document.body.firstChild, markup));
212                 sh.autoBoxAdjust = false;
213             }
214             return sh;
215         },
216
217         push : function(sh){
218             p.push(sh);
219         }
220     };
221 }();/*
222  * - LGPL
223  *
224  * base class for bootstrap elements.
225  * 
226  */
227
228 Roo.bootstrap = Roo.bootstrap || {};
229 /**
230  * @class Roo.bootstrap.Component
231  * @extends Roo.Component
232  * @abstract
233  * @children Roo.bootstrap.Component
234  * Bootstrap Component base class
235  * @cfg {String} cls css class
236  * @cfg {String} style any extra css
237  * @cfg {Object} xattr extra attributes to add to 'element' (used by builder to store stuff.)
238  * @cfg {Boolean} can_build_overlaid  True if element can be rebuild from a HTML page
239  * @cfg {string} dataId cutomer id
240  * @cfg {string} name Specifies name attribute
241  * @cfg {string} tooltip  Text for the tooltip
242  * @cfg {string} container_method method to fetch parents container element (used by NavHeaderbar -  getHeaderChildContainer)
243  * @cfg {string|object} visibilityEl (el|parent) What element to use for visibility (@see getVisibilityEl())
244  
245  * @constructor
246  * Do not use directly - it does not do anything..
247  * @param {Object} config The config object
248  */
249
250
251
252 Roo.bootstrap.Component = function(config){
253     Roo.bootstrap.Component.superclass.constructor.call(this, config);
254        
255     this.addEvents({
256         /**
257          * @event childrenrendered
258          * Fires when the children have been rendered..
259          * @param {Roo.bootstrap.Component} this
260          */
261         "childrenrendered" : true
262         
263         
264         
265     });
266     
267     
268 };
269
270 Roo.extend(Roo.bootstrap.Component, Roo.BoxComponent,  {
271     
272     
273     allowDomMove : false, // to stop relocations in parent onRender...
274     
275     cls : false,
276     
277     style : false,
278     
279     autoCreate : false,
280     
281     tooltip : null,
282     /**
283      * Initialize Events for the element
284      */
285     initEvents : function() { },
286     
287     xattr : false,
288     
289     parentId : false,
290     
291     can_build_overlaid : true,
292     
293     container_method : false,
294     
295     dataId : false,
296     
297     name : false,
298     
299     parent: function() {
300         // returns the parent component..
301         return Roo.ComponentMgr.get(this.parentId)
302         
303         
304     },
305     
306     // private
307     onRender : function(ct, position)
308     {
309        // Roo.log("Call onRender: " + this.xtype);
310         
311         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
312         
313         if(this.el){
314             if (this.el.attr('xtype')) {
315                 this.el.attr('xtypex', this.el.attr('xtype'));
316                 this.el.dom.removeAttribute('xtype');
317                 
318                 this.initEvents();
319             }
320             
321             return;
322         }
323         
324          
325         
326         var cfg = Roo.apply({},  this.getAutoCreate());
327         
328         cfg.id = this.id || Roo.id();
329         
330         // fill in the extra attributes 
331         if (this.xattr && typeof(this.xattr) =='object') {
332             for (var i in this.xattr) {
333                 cfg[i] = this.xattr[i];
334             }
335         }
336         
337         if(this.dataId){
338             cfg.dataId = this.dataId;
339         }
340         
341         if (this.cls) {
342             cfg.cls = (typeof(cfg.cls) == 'undefined' ? this.cls : cfg.cls) + ' ' + this.cls;
343         }
344         
345         if (this.style) { // fixme needs to support more complex style data.
346             cfg.style = (typeof(cfg.style) == 'undefined' ? this.style : cfg.style) + '; ' + this.style;
347         }
348         
349         if(this.name){
350             cfg.name = this.name;
351         }
352         
353         this.el = ct.createChild(cfg, position);
354         
355         if (this.tooltip) {
356             this.tooltipEl().attr('tooltip', this.tooltip);
357         }
358         
359         if(this.tabIndex !== undefined){
360             this.el.dom.setAttribute('tabIndex', this.tabIndex);
361         }
362         
363         this.initEvents();
364         
365     },
366     /**
367      * Fetch the element to add children to
368      * @return {Roo.Element} defaults to this.el
369      */
370     getChildContainer : function()
371     {
372         return this.el;
373     },
374     getDocumentBody : function() // used by menus - as they are attached to the body so zIndexes work
375     {
376         return Roo.get(document.body);
377     },
378     
379     /**
380      * Fetch the element to display the tooltip on.
381      * @return {Roo.Element} defaults to this.el
382      */
383     tooltipEl : function()
384     {
385         return this.el;
386     },
387         
388     addxtype  : function(tree,cntr)
389     {
390         var cn = this;
391         
392         cn = Roo.factory(tree);
393         //Roo.log(['addxtype', cn]);
394            
395         cn.parentType = this.xtype; //??
396         cn.parentId = this.id;
397         
398         cntr = (typeof(cntr) == 'undefined' ) ? 'getChildContainer' : cntr;
399         if (typeof(cn.container_method) == 'string') {
400             cntr = cn.container_method;
401         }
402         
403         
404         var has_flexy_each =  (typeof(tree['flexy:foreach']) != 'undefined');
405         
406         var has_flexy_if =  (typeof(tree['flexy:if']) != 'undefined');
407         
408         var build_from_html =  Roo.XComponent.build_from_html;
409           
410         var is_body  = (tree.xtype == 'Body') ;
411           
412         var page_has_body = (Roo.get(document.body).attr('xtype') == 'Roo.bootstrap.Body');
413           
414         var self_cntr_el = Roo.get(this[cntr](false));
415         
416         // do not try and build conditional elements 
417         if ((has_flexy_each || has_flexy_if || this.can_build_overlaid == false ) && build_from_html) {
418             return false;
419         }
420         
421         if (!has_flexy_each || !build_from_html || is_body || !page_has_body) {
422             if(!has_flexy_if || typeof(tree.name) == 'undefined' || !build_from_html || is_body || !page_has_body){
423                 return this.addxtypeChild(tree,cntr, is_body);
424             }
425             
426             var echild =self_cntr_el ? self_cntr_el.child('>*[name=' + tree.name + ']') : false;
427                 
428             if(echild){
429                 return this.addxtypeChild(Roo.apply({}, tree),cntr);
430             }
431             
432             Roo.log('skipping render');
433             return cn;
434             
435         }
436         
437         var ret = false;
438         if (!build_from_html) {
439             return false;
440         }
441         
442         // this i think handles overlaying multiple children of the same type
443         // with the sam eelement.. - which might be buggy..
444         while (true) {
445             var echild =self_cntr_el ? self_cntr_el.child('>*[xtype]') : false;
446             
447             if (!echild) {
448                 break;
449             }
450             
451             if (echild && echild.attr('xtype').split('.').pop() != cn.xtype) {
452                 break;
453             }
454             
455             ret = this.addxtypeChild(Roo.apply({}, tree),cntr);
456         }
457        
458         return ret;
459     },
460     
461     
462     addxtypeChild : function (tree, cntr, is_body)
463     {
464         Roo.debug && Roo.log('addxtypeChild:' + cntr);
465         var cn = this;
466         cntr = (typeof(cntr) == 'undefined' ) ? 'getChildContainer' : cntr;
467         
468         
469         var has_flexy = (typeof(tree['flexy:if']) != 'undefined') ||
470                     (typeof(tree['flexy:foreach']) != 'undefined');
471           
472     
473         
474         skip_children = false;
475         // render the element if it's not BODY.
476         if (!is_body) {
477             
478             // if parent was disabled, then do not try and create the children..
479             if(!this[cntr](true)){
480                 tree.items = [];
481                 return tree;
482             }
483            
484             cn = Roo.factory(tree);
485            
486             cn.parentType = this.xtype; //??
487             cn.parentId = this.id;
488             
489             var build_from_html =  Roo.XComponent.build_from_html;
490             
491             
492             // does the container contain child eleemnts with 'xtype' attributes.
493             // that match this xtype..
494             // note - when we render we create these as well..
495             // so we should check to see if body has xtype set.
496             if (build_from_html && Roo.get(document.body).attr('xtype') == 'Roo.bootstrap.Body') {
497                
498                 var self_cntr_el = Roo.get(this[cntr](false));
499                 var echild =self_cntr_el ? self_cntr_el.child('>*[xtype]') : false;
500                 if (echild) { 
501                     //Roo.log(Roo.XComponent.build_from_html);
502                     //Roo.log("got echild:");
503                     //Roo.log(echild);
504                 }
505                 // there is a scenario where some of the child elements are flexy:if (and all of the same type)
506                 // and are not displayed -this causes this to use up the wrong element when matching.
507                 // at present the only work around for this is to nest flexy:if elements in another element that is always rendered.
508                 
509                 
510                 if (echild && echild.attr('xtype').split('.').pop() == cn.xtype) {
511                   //  Roo.log("found child for " + this.xtype +": " + echild.attr('xtype') );
512                   
513                   
514                   
515                     cn.el = echild;
516                   //  Roo.log("GOT");
517                     //echild.dom.removeAttribute('xtype');
518                 } else {
519                     Roo.debug && Roo.log("MISSING " + cn.xtype + " on child of " + (this.el ? this.el.attr('xbuilderid') : 'no parent'));
520                     Roo.debug && Roo.log(self_cntr_el);
521                     Roo.debug && Roo.log(echild);
522                     Roo.debug && Roo.log(cn);
523                 }
524             }
525            
526             
527            
528             // if object has flexy:if - then it may or may not be rendered.
529             if (build_from_html && has_flexy && !cn.el &&  cn.can_build_overlaid) {
530                 // skip a flexy if element.
531                 Roo.debug && Roo.log('skipping render');
532                 Roo.debug && Roo.log(tree);
533                 if (!cn.el) {
534                     Roo.debug && Roo.log('skipping all children');
535                     skip_children = true;
536                 }
537                 
538              } else {
539                  
540                 // actually if flexy:foreach is found, we really want to create 
541                 // multiple copies here...
542                 //Roo.log('render');
543                 //Roo.log(this[cntr]());
544                 // some elements do not have render methods.. like the layouts...
545                 /*
546                 if(this[cntr](true) === false){
547                     cn.items = [];
548                     return cn;
549                 }
550                 */
551                 cn.render && cn.render(this[cntr](true));
552                 
553              }
554             // then add the element..
555         }
556          
557         // handle the kids..
558         
559         var nitems = [];
560         /*
561         if (typeof (tree.menu) != 'undefined') {
562             tree.menu.parentType = cn.xtype;
563             tree.menu.triggerEl = cn.el;
564             nitems.push(cn.addxtype(Roo.apply({}, tree.menu)));
565             
566         }
567         */
568         if (!tree.items || !tree.items.length) {
569             cn.items = nitems;
570             //Roo.log(["no children", this]);
571             
572             return cn;
573         }
574          
575         var items = tree.items;
576         delete tree.items;
577         
578         //Roo.log(items.length);
579             // add the items..
580         if (!skip_children) {    
581             for(var i =0;i < items.length;i++) {
582               //  Roo.log(['add child', items[i]]);
583                 nitems.push(cn.addxtype(items[i].xns == false ? items[i] : Roo.apply({}, items[i])));
584             }
585         }
586         
587         cn.items = nitems;
588         
589         //Roo.log("fire childrenrendered");
590         
591         cn.fireEvent('childrenrendered', this);
592         
593         return cn;
594     },
595     
596     /**
597      * Set the element that will be used to show or hide
598      */
599     setVisibilityEl : function(el)
600     {
601         this.visibilityEl = el;
602     },
603     
604      /**
605      * Get the element that will be used to show or hide
606      */
607     getVisibilityEl : function()
608     {
609         if (typeof(this.visibilityEl) == 'object') {
610             return this.visibilityEl;
611         }
612         
613         if (typeof(this.visibilityEl) == 'string') {
614             return this.visibilityEl == 'parent' ? this.parent().getEl() : this.getEl();
615         }
616         
617         return this.getEl();
618     },
619     
620     /**
621      * Show a component - removes 'hidden' class
622      */
623     show : function()
624     {
625         if(!this.getVisibilityEl()){
626             return;
627         }
628          
629         this.getVisibilityEl().removeClass(['hidden','d-none']);
630         
631         this.fireEvent('show', this);
632         
633         
634     },
635     /**
636      * Hide a component - adds 'hidden' class
637      */
638     hide: function()
639     {
640         if(!this.getVisibilityEl()){
641             return;
642         }
643         
644         this.getVisibilityEl().addClass(['hidden','d-none']);
645         
646         this.fireEvent('hide', this);
647         
648     }
649 });
650
651  /*
652  * - LGPL
653  *
654  * element
655  * 
656  */
657
658 /**
659  * @class Roo.bootstrap.Element
660  * @extends Roo.bootstrap.Component
661  * @children Roo.bootstrap.Component
662  * Bootstrap Element class (basically a DIV used to make random stuff )
663  * 
664  * @cfg {String} html contents of the element
665  * @cfg {String} tag tag of the element
666  * @cfg {String} cls class of the element
667  * @cfg {Boolean} preventDefault (true|false) default false
668  * @cfg {Boolean} clickable (true|false) default false
669  * @cfg {String} role default blank - set to button to force cursor pointer
670  
671  * 
672  * @constructor
673  * Create a new Element
674  * @param {Object} config The config object
675  */
676
677 Roo.bootstrap.Element = function(config){
678     Roo.bootstrap.Element.superclass.constructor.call(this, config);
679     
680     this.addEvents({
681         // raw events
682         /**
683          * @event click
684          * When a element is chick
685          * @param {Roo.bootstrap.Element} this
686          * @param {Roo.EventObject} e
687          */
688         "click" : true 
689         
690       
691     });
692 };
693
694 Roo.extend(Roo.bootstrap.Element, Roo.bootstrap.Component,  {
695     
696     tag: 'div',
697     cls: '',
698     html: '',
699     preventDefault: false, 
700     clickable: false,
701     tapedTwice : false,
702     role : false,
703     
704     getAutoCreate : function(){
705         
706         var cfg = {
707             tag: this.tag,
708             // cls: this.cls, double assign in parent class Component.js :: onRender
709             html: this.html
710         };
711         if (this.role !== false) {
712             cfg.role = this.role;
713         }
714         
715         return cfg;
716     },
717     
718     initEvents: function() 
719     {
720         Roo.bootstrap.Element.superclass.initEvents.call(this);
721         
722         if(this.clickable){
723             this.el.on('click', this.onClick, this);
724         }
725         
726         
727     },
728     
729     onClick : function(e)
730     {
731         if(this.preventDefault){
732             e.preventDefault();
733         }
734         
735         this.fireEvent('click', this, e); // why was this double click before?
736     },
737     
738     
739     
740
741     
742     
743     getValue : function()
744     {
745         return this.el.dom.innerHTML;
746     },
747     
748     setValue : function(value)
749     {
750         this.el.dom.innerHTML = value;
751     }
752    
753 });
754
755  
756
757  /*
758  * - LGPL
759  *
760  * dropable area
761  * 
762  */
763
764 /**
765  * @class Roo.bootstrap.DropTarget
766  * @extends Roo.bootstrap.Element
767  * Bootstrap DropTarget class
768  
769  * @cfg {string} name dropable name
770  * 
771  * @constructor
772  * Create a new Dropable Area
773  * @param {Object} config The config object
774  */
775
776 Roo.bootstrap.DropTarget = function(config){
777     Roo.bootstrap.DropTarget.superclass.constructor.call(this, config);
778     
779     this.addEvents({
780         // raw events
781         /**
782          * @event click
783          * When a element is chick
784          * @param {Roo.bootstrap.Element} this
785          * @param {Roo.EventObject} e
786          */
787         "drop" : true
788     });
789 };
790
791 Roo.extend(Roo.bootstrap.DropTarget, Roo.bootstrap.Element,  {
792     
793     
794     getAutoCreate : function(){
795         
796          
797     },
798     
799     initEvents: function() 
800     {
801         Roo.bootstrap.DropTarget.superclass.initEvents.call(this);
802         this.dropZone = new Roo.dd.DropTarget(this.getEl(), {
803             ddGroup: this.name,
804             listeners : {
805                 drop : this.dragDrop.createDelegate(this),
806                 enter : this.dragEnter.createDelegate(this),
807                 out : this.dragOut.createDelegate(this),
808                 over : this.dragOver.createDelegate(this)
809             }
810             
811         });
812         this.dropZone.DDM.useCache = false // so data gets refreshed when we resize stuff
813     },
814     
815     dragDrop : function(source,e,data)
816     {
817         // user has to decide how to impliment this.
818         Roo.log('drop');
819         Roo.log(this);
820         //this.fireEvent('drop', this, source, e ,data);
821         return false;
822     },
823     
824     dragEnter : function(n, dd, e, data)
825     {
826         // probably want to resize the element to match the dropped element..
827         Roo.log("enter");
828         this.originalSize = this.el.getSize();
829         this.el.setSize( n.el.getSize());
830         this.dropZone.DDM.refreshCache(this.name);
831         Roo.log([n, dd, e, data]);
832     },
833     
834     dragOut : function(value)
835     {
836         // resize back to normal
837         Roo.log("out");
838         this.el.setSize(this.originalSize);
839         this.dropZone.resetConstraints();
840     },
841     
842     dragOver : function()
843     {
844         // ??? do nothing?
845     }
846    
847 });
848
849  
850
851  /*
852  * - LGPL
853  *
854  * Body
855  *
856  */
857
858 /**
859  * @class Roo.bootstrap.Body
860  * @extends Roo.bootstrap.Component
861  * @children Roo.bootstrap.Component 
862  * @parent none builder
863  * Bootstrap Body class
864  *
865  * @constructor
866  * Create a new body
867  * @param {Object} config The config object
868  */
869
870 Roo.bootstrap.Body = function(config){
871
872     config = config || {};
873
874     Roo.bootstrap.Body.superclass.constructor.call(this, config);
875     this.el = Roo.get(config.el ? config.el : document.body );
876     if (this.cls && this.cls.length) {
877         Roo.get(document.body).addClass(this.cls);
878     }
879 };
880
881 Roo.extend(Roo.bootstrap.Body, Roo.bootstrap.Component,  {
882
883     is_body : true,// just to make sure it's constructed?
884
885         autoCreate : {
886         cls: 'container'
887     },
888     onRender : function(ct, position)
889     {
890        /* Roo.log("Roo.bootstrap.Body - onRender");
891         if (this.cls && this.cls.length) {
892             Roo.get(document.body).addClass(this.cls);
893         }
894         // style??? xttr???
895         */
896     }
897
898
899
900
901 });
902 /*
903  * - LGPL
904  *
905  * button group
906  * 
907  */
908
909
910 /**
911  * @class Roo.bootstrap.ButtonGroup
912  * @extends Roo.bootstrap.Component
913  * Bootstrap ButtonGroup class
914  * @children Roo.bootstrap.Button Roo.bootstrap.form.Form
915  * 
916  * @cfg {String} size lg | sm | xs (default empty normal)
917  * @cfg {String} align vertical | justified  (default none)
918  * @cfg {String} direction up | down (default down)
919  * @cfg {Boolean} toolbar false | true
920  * @cfg {Boolean} btn true | false
921  * 
922  * 
923  * @constructor
924  * Create a new Input
925  * @param {Object} config The config object
926  */
927
928 Roo.bootstrap.ButtonGroup = function(config){
929     Roo.bootstrap.ButtonGroup.superclass.constructor.call(this, config);
930 };
931
932 Roo.extend(Roo.bootstrap.ButtonGroup, Roo.bootstrap.Component,  {
933     
934     size: '',
935     align: '',
936     direction: '',
937     toolbar: false,
938     btn: true,
939
940     getAutoCreate : function(){
941         var cfg = {
942             cls: 'btn-group',
943             html : null
944         };
945         
946         cfg.html = this.html || cfg.html;
947         
948         if (this.toolbar) {
949             cfg = {
950                 cls: 'btn-toolbar',
951                 html: null
952             };
953             
954             return cfg;
955         }
956         
957         if (['vertical','justified'].indexOf(this.align)!==-1) {
958             cfg.cls = 'btn-group-' + this.align;
959             
960             if (this.align == 'justified') {
961                 console.log(this.items);
962             }
963         }
964         
965         if (['lg','sm','xs'].indexOf(this.size)!==-1) {
966             cfg.cls += ' btn-group-' + this.size;
967         }
968         
969         if (this.direction == 'up') {
970             cfg.cls += ' dropup' ;
971         }
972         
973         return cfg;
974     },
975     /**
976      * Add a button to the group (similar to NavItem API.)
977      */
978     addItem : function(cfg)
979     {
980         var cn = new Roo.bootstrap.Button(cfg);
981         //this.register(cn);
982         cn.parentId = this.id;
983         cn.onRender(this.el, null);
984         return cn;
985     }
986    
987 });
988
989  /*
990  * - LGPL
991  *
992  * button
993  * 
994  */
995
996 /**
997  * @class Roo.bootstrap.Button
998  * @extends Roo.bootstrap.Component
999  * Bootstrap Button class
1000  * @cfg {String} html The button content
1001  * @cfg {String} weight (default|primary|secondary|success|info|warning|danger|link|light|dark) default
1002  * @cfg {String} badge_weight (default|primary|secondary|success|info|warning|danger|link|light|dark) default (same as button)
1003  * @cfg {Boolean} outline default false (except for weight=default which emulates old behaveiour with an outline)
1004  * @cfg {String} size (lg|sm|xs)
1005  * @cfg {String} tag (a|input|submit)
1006  * @cfg {String} href empty or href
1007  * @cfg {Boolean} disabled default false;
1008  * @cfg {Boolean} isClose default false;
1009  * @cfg {String} glyphicon depricated - use fa
1010  * @cfg {String} fa fontawesome icon - eg. 'comment' - without the fa/fas etc..
1011  * @cfg {String} badge text for badge
1012  * @cfg {String} theme (default|glow)  
1013  * @cfg {Boolean} inverse dark themed version
1014  * @cfg {Boolean} toggle is it a slidy toggle button
1015  * @cfg {Boolean} pressed   default null - if the button ahs active state
1016  * @cfg {String} ontext text for on slidy toggle state
1017  * @cfg {String} offtext text for off slidy toggle state
1018  * @cfg {Boolean} preventDefault  default true (stop click event triggering the URL if it's a link.)
1019  * @cfg {Boolean} removeClass remove the standard class..
1020  * @cfg {String} target (_self|_blank|_parent|_top|other) target for a href. 
1021  * @cfg {Boolean} grpup if parent is a btn group - then it turns it into a toogleGroup.
1022  * @cfg {Roo.bootstrap.menu.Menu} menu a Menu 
1023
1024  * @constructor
1025  * Create a new button
1026  * @param {Object} config The config object
1027  */
1028
1029
1030 Roo.bootstrap.Button = function(config){
1031     Roo.bootstrap.Button.superclass.constructor.call(this, config);
1032     
1033     this.addEvents({
1034         // raw events
1035         /**
1036          * @event click
1037          * When a button is pressed
1038          * @param {Roo.bootstrap.Button} btn
1039          * @param {Roo.EventObject} e
1040          */
1041         "click" : true,
1042         /**
1043          * @event dblclick
1044          * When a button is double clicked
1045          * @param {Roo.bootstrap.Button} btn
1046          * @param {Roo.EventObject} e
1047          */
1048         "dblclick" : true,
1049          /**
1050          * @event toggle
1051          * After the button has been toggles
1052          * @param {Roo.bootstrap.Button} btn
1053          * @param {Roo.EventObject} e
1054          * @param {boolean} pressed (also available as button.pressed)
1055          */
1056         "toggle" : true
1057     });
1058 };
1059
1060 Roo.extend(Roo.bootstrap.Button, Roo.bootstrap.Component,  {
1061     html: false,
1062     active: false,
1063     weight: '',
1064     badge_weight: '',
1065     outline : false,
1066     size: '',
1067     tag: 'button',
1068     href: '',
1069     disabled: false,
1070     isClose: false,
1071     glyphicon: '',
1072     fa: '',
1073     badge: '',
1074     theme: 'default',
1075     inverse: false,
1076     
1077     toggle: false,
1078     ontext: 'ON',
1079     offtext: 'OFF',
1080     defaulton: true,
1081     preventDefault: true,
1082     removeClass: false,
1083     name: false,
1084     target: false,
1085     group : false,
1086      
1087     pressed : null,
1088      
1089     
1090     getAutoCreate : function(){
1091         
1092         var cfg = {
1093             tag : 'button',
1094             cls : 'roo-button',
1095             html: ''
1096         };
1097         
1098         if (['a', 'button', 'input', 'submit'].indexOf(this.tag) < 0) {
1099             throw "Invalid value for tag: " + this.tag + ". must be a, button, input or submit.";
1100             this.tag = 'button';
1101         } else {
1102             cfg.tag = this.tag;
1103         }
1104         cfg.html = '<span class="roo-button-text">' + (this.html || cfg.html) + '</span>';
1105         
1106         if (this.toggle == true) {
1107             cfg={
1108                 tag: 'div',
1109                 cls: 'slider-frame roo-button',
1110                 cn: [
1111                     {
1112                         tag: 'span',
1113                         'data-on-text':'ON',
1114                         'data-off-text':'OFF',
1115                         cls: 'slider-button',
1116                         html: this.offtext
1117                     }
1118                 ]
1119             };
1120             // why are we validating the weights?
1121             if (Roo.bootstrap.Button.weights.indexOf(this.weight) > -1) {
1122                 cfg.cls +=  ' ' + this.weight;
1123             }
1124             
1125             return cfg;
1126         }
1127         
1128         if (this.isClose) {
1129             cfg.cls += ' close';
1130             
1131             cfg["aria-hidden"] = true;
1132             
1133             cfg.html = "&times;";
1134             
1135             return cfg;
1136         }
1137              
1138         
1139         if (this.theme==='default') {
1140             cfg.cls = 'btn roo-button';
1141             
1142             //if (this.parentType != 'Navbar') {
1143             this.weight = this.weight.length ?  this.weight : 'default';
1144             //}
1145             if (Roo.bootstrap.Button.weights.indexOf(this.weight) > -1) {
1146                 
1147                 var outline = this.outline || this.weight == 'default' ? 'outline-' : '';
1148                 var weight = this.weight == 'default' ? 'secondary' : this.weight;
1149                 cfg.cls += ' btn-' + outline + weight;
1150                 if (this.weight == 'default') {
1151                     // BC
1152                     cfg.cls += ' btn-' + this.weight;
1153                 }
1154             }
1155         } else if (this.theme==='glow') {
1156             
1157             cfg.tag = 'a';
1158             cfg.cls = 'btn-glow roo-button';
1159             
1160             if (Roo.bootstrap.Button.weights.indexOf(this.weight) > -1) {
1161                 
1162                 cfg.cls += ' ' + this.weight;
1163             }
1164         }
1165    
1166         
1167         if (this.inverse) {
1168             this.cls += ' inverse';
1169         }
1170         
1171         
1172         if (this.active || this.pressed === true) {
1173             cfg.cls += ' active';
1174         }
1175         
1176         if (this.disabled) {
1177             cfg.disabled = 'disabled';
1178         }
1179         
1180         if (this.items) {
1181             Roo.log('changing to ul' );
1182             cfg.tag = 'ul';
1183             this.glyphicon = 'caret';
1184             if (Roo.bootstrap.version == 4) {
1185                 this.fa = 'caret-down';
1186             }
1187             
1188         }
1189         
1190         cfg.cls += this.size.length ? (' btn-' + this.size) : '';
1191          
1192         //gsRoo.log(this.parentType);
1193         if (this.parentType === 'Navbar' && !this.parent().bar) {
1194             Roo.log('changing to li?');
1195             
1196             cfg.tag = 'li';
1197             
1198             cfg.cls = '';
1199             cfg.cn =  [{
1200                 tag : 'a',
1201                 cls : 'roo-button',
1202                 html : this.html,
1203                 href : this.href || '#'
1204             }];
1205             if (this.menu) {
1206                 cfg.cn[0].html = this.html  + ' <span class="caret"></span>';
1207                 cfg.cls += ' dropdown';
1208             }   
1209             
1210             delete cfg.html;
1211             
1212         }
1213         
1214        cfg.cls += this.parentType === 'Navbar' ?  ' navbar-btn' : '';
1215         
1216         if (this.glyphicon) {
1217             cfg.html = ' ' + cfg.html;
1218             
1219             cfg.cn = [
1220                 {
1221                     tag: 'span',
1222                     cls: 'glyphicon glyphicon-' + this.glyphicon
1223                 }
1224             ];
1225         }
1226         if (this.fa) {
1227             cfg.html = ' ' + cfg.html;
1228             
1229             cfg.cn = [
1230                 {
1231                     tag: 'i',
1232                     cls: 'fa fas fa-' + this.fa
1233                 }
1234             ];
1235         }
1236         
1237         if (this.badge) {
1238             cfg.html += ' ';
1239             
1240             cfg.tag = 'a';
1241             
1242 //            cfg.cls='btn roo-button';
1243             
1244             cfg.href=this.href;
1245             
1246             var value = cfg.html;
1247             
1248             if(this.glyphicon){
1249                 value = {
1250                     tag: 'span',
1251                     cls: 'glyphicon glyphicon-' + this.glyphicon,
1252                     html: this.html
1253                 };
1254             }
1255             if(this.fa){
1256                 value = {
1257                     tag: 'i',
1258                     cls: 'fa fas fa-' + this.fa,
1259                     html: this.html
1260                 };
1261             }
1262             
1263             var bw = this.badge_weight.length ? this.badge_weight :
1264                 (this.weight.length ? this.weight : 'secondary');
1265             bw = bw == 'default' ? 'secondary' : bw;
1266             
1267             cfg.cn = [
1268                 value,
1269                 {
1270                     tag: 'span',
1271                     cls: 'badge badge-' + bw,
1272                     html: this.badge
1273                 }
1274             ];
1275             
1276             cfg.html='';
1277         }
1278         
1279         if (this.menu) {
1280             cfg.cls += ' dropdown';
1281             cfg.html = typeof(cfg.html) != 'undefined' ?
1282                     cfg.html + ' <span class="caret"></span>' : '<span class="caret"></span>';
1283         }
1284         
1285         if (cfg.tag !== 'a' && this.href !== '') {
1286             throw "Tag must be a to set href.";
1287         } else if (this.href.length > 0) {
1288             cfg.href = this.href;
1289         }
1290         
1291         if(this.removeClass){
1292             cfg.cls = '';
1293         }
1294         
1295         if(this.target){
1296             cfg.target = this.target;
1297         }
1298         
1299         return cfg;
1300     },
1301     initEvents: function() {
1302        // Roo.log('init events?');
1303 //        Roo.log(this.el.dom);
1304         // add the menu...
1305         
1306         if (typeof (this.menu) != 'undefined') {
1307             this.menu.parentType = this.xtype;
1308             this.menu.triggerEl = this.el;
1309             this.addxtype(Roo.apply({}, this.menu));
1310         }
1311
1312
1313         if (this.el.hasClass('roo-button')) {
1314              this.el.on('click', this.onClick, this);
1315              this.el.on('dblclick', this.onDblClick, this);
1316         } else {
1317              this.el.select('.roo-button').on('click', this.onClick, this);
1318              this.el.select('.roo-button').on('dblclick', this.onDblClick, this);
1319              
1320         }
1321         // why?
1322         if(this.removeClass){
1323             this.el.on('click', this.onClick, this);
1324         }
1325         
1326         if (this.group === true) {
1327              if (this.pressed === false || this.pressed === true) {
1328                 // nothing
1329             } else {
1330                 this.pressed = false;
1331                 this.setActive(this.pressed);
1332             }
1333             
1334         }
1335         
1336         this.el.enableDisplayMode();
1337         
1338     },
1339     onClick : function(e)
1340     {
1341         if (this.disabled) {
1342             return;
1343         }
1344         
1345         Roo.log('button on click ');
1346         if(this.href === '' || this.preventDefault){
1347             e.preventDefault();
1348         }
1349         
1350         if (this.group) {
1351             if (this.pressed) {
1352                 // do nothing -
1353                 return;
1354             }
1355             this.setActive(true);
1356             var pi = this.parent().items;
1357             for (var i = 0;i < pi.length;i++) {
1358                 if (this == pi[i]) {
1359                     continue;
1360                 }
1361                 if (pi[i].el.hasClass('roo-button')) {
1362                     pi[i].setActive(false);
1363                 }
1364             }
1365             this.fireEvent('click', this, e);            
1366             return;
1367         }
1368         
1369         if (this.pressed === true || this.pressed === false) {
1370             this.toggleActive(e);
1371         }
1372         
1373         
1374         this.fireEvent('click', this, e);
1375     },
1376     onDblClick: function(e)
1377     {
1378         if (this.disabled) {
1379             return;
1380         }
1381         if(this.preventDefault){
1382             e.preventDefault();
1383         }
1384         this.fireEvent('dblclick', this, e);
1385     },
1386     /**
1387      * Enables this button
1388      */
1389     enable : function()
1390     {
1391         this.disabled = false;
1392         this.el.removeClass('disabled');
1393         this.el.dom.removeAttribute("disabled");
1394     },
1395     
1396     /**
1397      * Disable this button
1398      */
1399     disable : function()
1400     {
1401         this.disabled = true;
1402         this.el.addClass('disabled');
1403         this.el.attr("disabled", "disabled")
1404     },
1405      /**
1406      * sets the active state on/off, 
1407      * @param {Boolean} state (optional) Force a particular state
1408      */
1409     setActive : function(v) {
1410         
1411         this.el[v ? 'addClass' : 'removeClass']('active');
1412         this.pressed = v;
1413     },
1414      /**
1415      * toggles the current active state 
1416      */
1417     toggleActive : function(e)
1418     {
1419         this.setActive(!this.pressed); // this modifies pressed...
1420         this.fireEvent('toggle', this, e, this.pressed);
1421     },
1422      /**
1423      * get the current active state
1424      * @return {boolean} true if it's active
1425      */
1426     isActive : function()
1427     {
1428         return this.el.hasClass('active');
1429     },
1430     /**
1431      * set the text of the first selected button
1432      */
1433     setText : function(str)
1434     {
1435         this.el.select('.roo-button-text',true).first().dom.innerHTML = str;
1436     },
1437     /**
1438      * get the text of the first selected button
1439      */
1440     getText : function()
1441     {
1442         return this.el.select('.roo-button-text',true).first().dom.innerHTML;
1443     },
1444     
1445     setWeight : function(str)
1446     {
1447         this.el.removeClass(Roo.bootstrap.Button.weights.map(function(w) { return 'btn-' + w; } ) );
1448         this.el.removeClass(Roo.bootstrap.Button.weights.map(function(w) { return 'btn-outline-' + w; } ) );
1449         this.weight = str;
1450         var outline = this.outline ? 'outline-' : '';
1451         if (str == 'default') {
1452             this.el.addClass('btn-default btn-outline-secondary');        
1453             return;
1454         }
1455         this.el.addClass('btn-' + outline + str);        
1456     }
1457     
1458     
1459 });
1460 // fixme - this is probably generic bootstrap - should go in some kind of enum file.. - like sizes.
1461
1462 Roo.bootstrap.Button.weights = [
1463     'default',
1464     'secondary' ,
1465     'primary',
1466     'success',
1467     'info',
1468     'warning',
1469     'danger',
1470     'link',
1471     'light',
1472     'dark'              
1473    
1474 ];/*
1475  * - LGPL
1476  *
1477  * column
1478  * 
1479  */
1480
1481 /**
1482  * @class Roo.bootstrap.Column
1483  * @extends Roo.bootstrap.Component
1484  * @children Roo.bootstrap.Component
1485  * Bootstrap Column class
1486  * @cfg {Number} xs colspan out of 12 for mobile-sized screens or 0 for hidden
1487  * @cfg {Number} sm colspan out of 12 for tablet-sized screens or 0 for hidden
1488  * @cfg {Number} md colspan out of 12 for computer-sized screens or 0 for hidden
1489  * @cfg {Number} lg colspan out of 12 for large computer-sized screens or 0 for hidden
1490  * @cfg {Number} xsoff colspan offset out of 12 for mobile-sized screens or 0 for hidden
1491  * @cfg {Number} smoff colspan offset out of 12 for tablet-sized screens or 0 for hidden
1492  * @cfg {Number} mdoff colspan offset out of 12 for computer-sized screens or 0 for hidden
1493  * @cfg {Number} lgoff colspan offset out of 12 for large computer-sized screens or 0 for hidden
1494  *
1495  * 
1496  * @cfg {Boolean} hidden (true|false) hide the element
1497  * @cfg {String} alert (success|info|warning|danger) type alert (changes background / border...)
1498  * @cfg {String} fa (ban|check|...) font awesome icon
1499  * @cfg {Number} fasize (1|2|....) font awsome size
1500
1501  * @cfg {String} icon (info-sign|check|...) glyphicon name
1502
1503  * @cfg {String} html content of column.
1504  * 
1505  * @constructor
1506  * Create a new Column
1507  * @param {Object} config The config object
1508  */
1509
1510 Roo.bootstrap.Column = function(config){
1511     Roo.bootstrap.Column.superclass.constructor.call(this, config);
1512 };
1513
1514 Roo.extend(Roo.bootstrap.Column, Roo.bootstrap.Component,  {
1515     
1516     xs: false,
1517     sm: false,
1518     md: false,
1519     lg: false,
1520     xsoff: false,
1521     smoff: false,
1522     mdoff: false,
1523     lgoff: false,
1524     html: '',
1525     offset: 0,
1526     alert: false,
1527     fa: false,
1528     icon : false,
1529     hidden : false,
1530     fasize : 1,
1531     
1532     getAutoCreate : function(){
1533         var cfg = Roo.apply({}, Roo.bootstrap.Column.superclass.getAutoCreate.call(this));
1534         
1535         cfg = {
1536             tag: 'div',
1537             cls: 'column'
1538         };
1539         
1540         var settings=this;
1541         var sizes =   ['xs','sm','md','lg'];
1542         sizes.map(function(size ,ix){
1543             //Roo.log( size + ':' + settings[size]);
1544             
1545             if (settings[size+'off'] !== false) {
1546                 cfg.cls += ' col-' + size + '-offset-' + settings[size+'off'] ;
1547             }
1548             
1549             if (settings[size] === false) {
1550                 return;
1551             }
1552             
1553             if (!settings[size]) { // 0 = hidden
1554                 cfg.cls += ' hidden-' + size + ' hidden-' + size + '-down';
1555                 // bootsrap4
1556                 for (var i = ix; i > -1; i--) {
1557                     cfg.cls +=  ' d-' + sizes[i] + '-none'; 
1558                 }
1559                 
1560                 
1561                 return;
1562             }
1563             cfg.cls += ' col-' + size + '-' + settings[size] + (
1564                 size == 'xs' ? (' col-' + settings[size] ) : '' // bs4 col-{num} replaces col-xs
1565             );
1566             
1567         });
1568         
1569         if (this.hidden) {
1570             cfg.cls += ' hidden';
1571         }
1572         
1573         if (this.alert && ["success","info","warning", "danger"].indexOf(this.alert) > -1) {
1574             cfg.cls +=' alert alert-' + this.alert;
1575         }
1576         
1577         
1578         if (this.html.length) {
1579             cfg.html = this.html;
1580         }
1581         if (this.fa) {
1582             var fasize = '';
1583             if (this.fasize > 1) {
1584                 fasize = ' fa-' + this.fasize + 'x';
1585             }
1586             cfg.html = '<i class="fa fa-'+this.fa + fasize + '"></i>' + (cfg.html || '');
1587             
1588             
1589         }
1590         if (this.icon) {
1591             cfg.html = '<i class="glyphicon glyphicon-'+this.icon + '"></i>' +  (cfg.html || '');
1592         }
1593         
1594         return cfg;
1595     }
1596    
1597 });
1598
1599  
1600
1601  /*
1602  * - LGPL
1603  *
1604  * page container.
1605  * 
1606  */
1607
1608
1609 /**
1610  * @class Roo.bootstrap.Container
1611  * @extends Roo.bootstrap.Component
1612  * @children Roo.bootstrap.Component
1613  * @parent builder
1614  * Bootstrap Container class
1615  * @cfg {Boolean} jumbotron is it a jumbotron element
1616  * @cfg {String} html content of element
1617  * @cfg {String} well (lg|sm|md) a well, large, small or medium.
1618  * @cfg {String} panel (default|primary|success|info|warning|danger) render as panel  - type - primary/success.....
1619  * @cfg {String} header content of header (for panel)
1620  * @cfg {String} footer content of footer (for panel)
1621  * @cfg {String} sticky (footer|wrap|push) block to use as footer or body- needs css-bootstrap/sticky-footer.css
1622  * @cfg {String} tag (header|aside|section) type of HTML tag.
1623  * @cfg {String} alert (success|info|warning|danger) type alert (changes background / border...)
1624  * @cfg {String} fa font awesome icon
1625  * @cfg {String} icon (info-sign|check|...) glyphicon name
1626  * @cfg {Boolean} hidden (true|false) hide the element
1627  * @cfg {Boolean} expandable (true|false) default false
1628  * @cfg {Boolean} expanded (true|false) default true
1629  * @cfg {String} rheader contet on the right of header
1630  * @cfg {Boolean} clickable (true|false) default false
1631
1632  *     
1633  * @constructor
1634  * Create a new Container
1635  * @param {Object} config The config object
1636  */
1637
1638 Roo.bootstrap.Container = function(config){
1639     Roo.bootstrap.Container.superclass.constructor.call(this, config);
1640     
1641     this.addEvents({
1642         // raw events
1643          /**
1644          * @event expand
1645          * After the panel has been expand
1646          * 
1647          * @param {Roo.bootstrap.Container} this
1648          */
1649         "expand" : true,
1650         /**
1651          * @event collapse
1652          * After the panel has been collapsed
1653          * 
1654          * @param {Roo.bootstrap.Container} this
1655          */
1656         "collapse" : true,
1657         /**
1658          * @event click
1659          * When a element is chick
1660          * @param {Roo.bootstrap.Container} this
1661          * @param {Roo.EventObject} e
1662          */
1663         "click" : true
1664     });
1665 };
1666
1667 Roo.extend(Roo.bootstrap.Container, Roo.bootstrap.Component,  {
1668     
1669     jumbotron : false,
1670     well: '',
1671     panel : '',
1672     header: '',
1673     footer : '',
1674     sticky: '',
1675     tag : false,
1676     alert : false,
1677     fa: false,
1678     icon : false,
1679     expandable : false,
1680     rheader : '',
1681     expanded : true,
1682     clickable: false,
1683   
1684      
1685     getChildContainer : function() {
1686         
1687         if(!this.el){
1688             return false;
1689         }
1690         
1691         if (this.panel.length) {
1692             return this.el.select('.panel-body',true).first();
1693         }
1694         
1695         return this.el;
1696     },
1697     
1698     
1699     getAutoCreate : function(){
1700         
1701         var cfg = {
1702             tag : this.tag || 'div',
1703             html : '',
1704             cls : ''
1705         };
1706         if (this.jumbotron) {
1707             cfg.cls = 'jumbotron';
1708         }
1709         
1710         
1711         
1712         // - this is applied by the parent..
1713         //if (this.cls) {
1714         //    cfg.cls = this.cls + '';
1715         //}
1716         
1717         if (this.sticky.length) {
1718             
1719             var bd = Roo.get(document.body);
1720             if (!bd.hasClass('bootstrap-sticky')) {
1721                 bd.addClass('bootstrap-sticky');
1722                 Roo.select('html',true).setStyle('height', '100%');
1723             }
1724              
1725             cfg.cls += 'bootstrap-sticky-' + this.sticky;
1726         }
1727         
1728         
1729         if (this.well.length) {
1730             switch (this.well) {
1731                 case 'lg':
1732                 case 'sm':
1733                     cfg.cls +=' well well-' +this.well;
1734                     break;
1735                 default:
1736                     cfg.cls +=' well';
1737                     break;
1738             }
1739         }
1740         
1741         if (this.hidden) {
1742             cfg.cls += ' hidden';
1743         }
1744         
1745         
1746         if (this.alert && ["success","info","warning", "danger"].indexOf(this.alert) > -1) {
1747             cfg.cls +=' alert alert-' + this.alert;
1748         }
1749         
1750         var body = cfg;
1751         
1752         if (this.panel.length) {
1753             cfg.cls += ' panel panel-' + this.panel;
1754             cfg.cn = [];
1755             if (this.header.length) {
1756                 
1757                 var h = [];
1758                 
1759                 if(this.expandable){
1760                     
1761                     cfg.cls = cfg.cls + ' expandable';
1762                     
1763                     h.push({
1764                         tag: 'i',
1765                         cls: (this.expanded ? 'fa fa-minus' : 'fa fa-plus') 
1766                     });
1767                     
1768                 }
1769                 
1770                 h.push(
1771                     {
1772                         tag: 'span',
1773                         cls : 'panel-title',
1774                         html : (this.expandable ? '&nbsp;' : '') + this.header
1775                     },
1776                     {
1777                         tag: 'span',
1778                         cls: 'panel-header-right',
1779                         html: this.rheader
1780                     }
1781                 );
1782                 
1783                 cfg.cn.push({
1784                     cls : 'panel-heading',
1785                     style : this.expandable ? 'cursor: pointer' : '',
1786                     cn : h
1787                 });
1788                 
1789             }
1790             
1791             body = false;
1792             cfg.cn.push({
1793                 cls : 'panel-body' + (this.expanded ? '' : ' hide'),
1794                 html : this.html
1795             });
1796             
1797             
1798             if (this.footer.length) {
1799                 cfg.cn.push({
1800                     cls : 'panel-footer',
1801                     html : this.footer
1802                     
1803                 });
1804             }
1805             
1806         }
1807         
1808         if (body) {
1809             body.html = this.html || cfg.html;
1810             // prefix with the icons..
1811             if (this.fa) {
1812                 body.html = '<i class="fa fa-'+this.fa + '"></i>' + body.html ;
1813             }
1814             if (this.icon) {
1815                 body.html = '<i class="glyphicon glyphicon-'+this.icon + '"></i>' + body.html ;
1816             }
1817             
1818             
1819         }
1820         if ((!this.cls || !this.cls.length) && (!cfg.cls || !cfg.cls.length)) {
1821             cfg.cls =  'container';
1822         }
1823         
1824         return cfg;
1825     },
1826     
1827     initEvents: function() 
1828     {
1829         if(this.expandable){
1830             var headerEl = this.headerEl();
1831         
1832             if(headerEl){
1833                 headerEl.on('click', this.onToggleClick, this);
1834             }
1835         }
1836         
1837         if(this.clickable){
1838             this.el.on('click', this.onClick, this);
1839         }
1840         
1841     },
1842     
1843     onToggleClick : function()
1844     {
1845         var headerEl = this.headerEl();
1846         
1847         if(!headerEl){
1848             return;
1849         }
1850         
1851         if(this.expanded){
1852             this.collapse();
1853             return;
1854         }
1855         
1856         this.expand();
1857     },
1858     
1859     expand : function()
1860     {
1861         if(this.fireEvent('expand', this)) {
1862             
1863             this.expanded = true;
1864             
1865             //this.el.select('.panel-body',true).first().setVisibilityMode(Roo.Element.DISPLAY).show();
1866             
1867             this.el.select('.panel-body',true).first().removeClass('hide');
1868             
1869             var toggleEl = this.toggleEl();
1870
1871             if(!toggleEl){
1872                 return;
1873             }
1874
1875             toggleEl.removeClass(['fa-minus', 'fa-plus']).addClass(['fa-minus']);
1876         }
1877         
1878     },
1879     
1880     collapse : function()
1881     {
1882         if(this.fireEvent('collapse', this)) {
1883             
1884             this.expanded = false;
1885             
1886             //this.el.select('.panel-body',true).first().setVisibilityMode(Roo.Element.DISPLAY).hide();
1887             this.el.select('.panel-body',true).first().addClass('hide');
1888         
1889             var toggleEl = this.toggleEl();
1890
1891             if(!toggleEl){
1892                 return;
1893             }
1894
1895             toggleEl.removeClass(['fa-minus', 'fa-plus']).addClass(['fa-plus']);
1896         }
1897     },
1898     
1899     toggleEl : function()
1900     {
1901         if(!this.el || !this.panel.length || !this.header.length || !this.expandable){
1902             return;
1903         }
1904         
1905         return this.el.select('.panel-heading .fa',true).first();
1906     },
1907     
1908     headerEl : function()
1909     {
1910         if(!this.el || !this.panel.length || !this.header.length){
1911             return;
1912         }
1913         
1914         return this.el.select('.panel-heading',true).first()
1915     },
1916     
1917     bodyEl : function()
1918     {
1919         if(!this.el || !this.panel.length){
1920             return;
1921         }
1922         
1923         return this.el.select('.panel-body',true).first()
1924     },
1925     
1926     titleEl : function()
1927     {
1928         if(!this.el || !this.panel.length || !this.header.length){
1929             return;
1930         }
1931         
1932         return this.el.select('.panel-title',true).first();
1933     },
1934     
1935     setTitle : function(v)
1936     {
1937         var titleEl = this.titleEl();
1938         
1939         if(!titleEl){
1940             return;
1941         }
1942         
1943         titleEl.dom.innerHTML = v;
1944     },
1945     
1946     getTitle : function()
1947     {
1948         
1949         var titleEl = this.titleEl();
1950         
1951         if(!titleEl){
1952             return '';
1953         }
1954         
1955         return titleEl.dom.innerHTML;
1956     },
1957     
1958     setRightTitle : function(v)
1959     {
1960         var t = this.el.select('.panel-header-right',true).first();
1961         
1962         if(!t){
1963             return;
1964         }
1965         
1966         t.dom.innerHTML = v;
1967     },
1968     
1969     onClick : function(e)
1970     {
1971         e.preventDefault();
1972         
1973         this.fireEvent('click', this, e);
1974     }
1975 });
1976
1977  /**
1978  * @class Roo.bootstrap.Card
1979  * @extends Roo.bootstrap.Component
1980  * @children Roo.bootstrap.Component
1981  * @licence LGPL
1982  * Bootstrap Card class - note this has children as CardHeader/ImageTop/Footer.. - which should really be listed properties?
1983  *
1984  *
1985  * possible... may not be implemented..
1986  * @cfg {String} header_image  src url of image.
1987  * @cfg {String|Object} header
1988  * @cfg {Number} header_size (0|1|2|3|4|5) H1 or H2 etc.. 0 indicates default
1989  * @cfg {Number} header_weight  (primary|secondary|success|info|warning|danger|light|dark)
1990  * 
1991  * @cfg {String} title
1992  * @cfg {String} subtitle
1993  * @cfg {String|Boolean} html -- html contents - or just use children.. use false to hide it..
1994  * @cfg {String} footer
1995  
1996  * @cfg {String} weight (primary|warning|info|danger|secondary|success|light|dark)
1997  * 
1998  * @cfg {String} margin (0|1|2|3|4|5|auto)
1999  * @cfg {String} margin_top (0|1|2|3|4|5|auto)
2000  * @cfg {String} margin_bottom (0|1|2|3|4|5|auto)
2001  * @cfg {String} margin_left (0|1|2|3|4|5|auto)
2002  * @cfg {String} margin_right (0|1|2|3|4|5|auto)
2003  * @cfg {String} margin_x (0|1|2|3|4|5|auto)
2004  * @cfg {String} margin_y (0|1|2|3|4|5|auto)
2005  *
2006  * @cfg {String} padding (0|1|2|3|4|5)
2007  * @cfg {String} padding_top (0|1|2|3|4|5)next_to_card
2008  * @cfg {String} padding_bottom (0|1|2|3|4|5)
2009  * @cfg {String} padding_left (0|1|2|3|4|5)
2010  * @cfg {String} padding_right (0|1|2|3|4|5)
2011  * @cfg {String} padding_x (0|1|2|3|4|5)
2012  * @cfg {String} padding_y (0|1|2|3|4|5)
2013  *
2014  * @cfg {String} display (none|inline|inline-block|block|table|table-cell|table-row|flex|inline-flex)
2015  * @cfg {String} display_xs (none|inline|inline-block|block|table|table-cell|table-row|flex|inline-flex)
2016  * @cfg {String} display_sm (none|inline|inline-block|block|table|table-cell|table-row|flex|inline-flex)
2017  * @cfg {String} display_lg (none|inline|inline-block|block|table|table-cell|table-row|flex|inline-flex)
2018  * @cfg {String} display_xl (none|inline|inline-block|block|table|table-cell|table-row|flex|inline-flex)
2019  
2020  * @config {Boolean} dragable  if this card can be dragged.
2021  * @config {String} drag_group  group for drag
2022  * @config {Boolean} dropable  if this card can recieve other cards being dropped onto it..
2023  * @config {String} drop_group  group for drag
2024  * 
2025  * @config {Boolean} collapsable can the body be collapsed.
2026  * @config {Boolean} collapsed is the body collapsed when rendered...
2027  * @config {Boolean} rotateable can the body be rotated by clicking on it..
2028  * @config {Boolean} rotated is the body rotated when rendered...
2029  * 
2030  * @constructor
2031  * Create a new Container
2032  * @param {Object} config The config object
2033  */
2034
2035 Roo.bootstrap.Card = function(config){
2036     Roo.bootstrap.Card.superclass.constructor.call(this, config);
2037     
2038     this.addEvents({
2039          // raw events
2040         /**
2041          * @event drop
2042          * When a element a card is dropped
2043          * @param {Roo.bootstrap.Card} this
2044          *
2045          * 
2046          * @param {Roo.bootstrap.Card} move_card the card being dropped?
2047          * @param {String} position 'above' or 'below'
2048          * @param {Roo.bootstrap.Card} next_to_card What card position is relative to of 'false' for empty list.
2049         
2050          */
2051         'drop' : true,
2052          /**
2053          * @event rotate
2054          * When a element a card is rotate
2055          * @param {Roo.bootstrap.Card} this
2056          * @param {Roo.Element} n the node being dropped?
2057          * @param {Boolean} rotate status
2058          */
2059         'rotate' : true,
2060         /**
2061          * @event cardover
2062          * When a card element is dragged over ready to drop (return false to block dropable)
2063          * @param {Roo.bootstrap.Card} this
2064          * @param {Object} data from dragdrop 
2065          */
2066          'cardover' : true
2067          
2068     });
2069 };
2070
2071
2072 Roo.extend(Roo.bootstrap.Card, Roo.bootstrap.Component,  {
2073     
2074     
2075     weight : '',
2076     
2077     margin: '', /// may be better in component?
2078     margin_top: '', 
2079     margin_bottom: '', 
2080     margin_left: '',
2081     margin_right: '',
2082     margin_x: '',
2083     margin_y: '',
2084     
2085     padding : '',
2086     padding_top: '', 
2087     padding_bottom: '', 
2088     padding_left: '',
2089     padding_right: '',
2090     padding_x: '',
2091     padding_y: '',
2092     
2093     display: '', 
2094     display_xs: '', 
2095     display_sm: '', 
2096     display_lg: '',
2097     display_xl: '',
2098  
2099     header_image  : '',
2100     header : '',
2101     header_size : 0,
2102     title : '',
2103     subtitle : '',
2104     html : '',
2105     footer: '',
2106
2107     collapsable : false,
2108     collapsed : false,
2109     rotateable : false,
2110     rotated : false,
2111     
2112     dragable : false,
2113     drag_group : false,
2114     dropable : false,
2115     drop_group : false,
2116     childContainer : false,
2117     dropEl : false, /// the dom placeholde element that indicates drop location.
2118     containerEl: false, // body container
2119     bodyEl: false, // card-body
2120     headerContainerEl : false, //
2121     headerEl : false,
2122     header_imageEl : false,
2123     
2124     
2125     layoutCls : function()
2126     {
2127         var cls = '';
2128         var t = this;
2129         Roo.log(this.margin_bottom.length);
2130         ['', 'top', 'bottom', 'left', 'right', 'x', 'y' ].forEach(function(v) {
2131             // in theory these can do margin_top : ml-xs-3 ??? but we don't support that yet
2132             
2133             if (('' + t['margin' + (v.length ? '_' : '') + v]).length) {
2134                 cls += ' m' +  (v.length ? v[0]  : '') + '-' +  t['margin' + (v.length ? '_' : '') + v];
2135             }
2136             if (('' + t['padding' + (v.length ? '_' : '') + v]).length) {
2137                 cls += ' p' +  (v.length ? v[0]  : '') + '-' +  t['padding' + (v.length ? '_' : '') + v];
2138             }
2139         });
2140         
2141         ['', 'xs', 'sm', 'lg', 'xl'].forEach(function(v) {
2142             if (('' + t['display' + (v.length ? '_' : '') + v]).length) {
2143                 cls += ' d' +  (v.length ? '-' : '') + v + '-' + t['display' + (v.length ? '_' : '') + v]
2144             }
2145         });
2146         
2147         // more generic support?
2148         if (this.hidden) {
2149             cls += ' d-none';
2150         }
2151         
2152         return cls;
2153     },
2154  
2155        // Roo.log("Call onRender: " + this.xtype);
2156         /*  We are looking at something like this.
2157 <div class="card">
2158     <img src="..." class="card-img-top" alt="...">
2159     <div class="card-body">
2160         <h5 class="card-title">Card title</h5>
2161          <h6 class="card-subtitle mb-2 text-muted">Card subtitle</h6>
2162
2163         >> this bit is really the body...
2164         <div> << we will ad dthis in hopefully it will not break shit.
2165         
2166         ** card text does not actually have any styling...
2167         
2168             <p class="card-text">This is a wider card with supporting text below as a natural lead-in to additional content. This content is a little bit longer.</p>
2169         
2170         </div> <<
2171           <a href="#" class="card-link">Card link</a>
2172           
2173     </div>
2174     <div class="card-footer">
2175         <small class="text-muted">Last updated 3 mins ago</small>
2176     </div>
2177 </div>
2178          */
2179     getAutoCreate : function(){
2180         
2181         var cfg = {
2182             tag : 'div',
2183             cls : 'card',
2184             cn : [ ]
2185         };
2186         
2187         if (this.weight.length && this.weight != 'light') {
2188             cfg.cls += ' text-white';
2189         } else {
2190             cfg.cls += ' text-dark'; // need as it's nested..
2191         }
2192         if (this.weight.length) {
2193             cfg.cls += ' bg-' + this.weight;
2194         }
2195         
2196         cfg.cls += ' ' + this.layoutCls(); 
2197         
2198         var hdr = false;
2199         var hdr_ctr = false;
2200         if (this.header.length) {
2201             hdr = {
2202                 tag : this.header_size > 0 ? 'h' + this.header_size : 'div',
2203                 cls : 'card-header ' + (this.header_weight ? 'bg-' + this.header_weight : ''),
2204                 cn : []
2205             };
2206             cfg.cn.push(hdr);
2207             hdr_ctr = hdr;
2208         } else {
2209             hdr = {
2210                 tag : 'div',
2211                 cls : 'card-header d-none ' + (this.header_weight ? 'bg-' + this.header_weight : ''),
2212                 cn : []
2213             };
2214             cfg.cn.push(hdr);
2215             hdr_ctr = hdr;
2216         }
2217         if (this.collapsable) {
2218             hdr_ctr = {
2219             tag : 'a',
2220             cls : 'd-block user-select-none',
2221             cn: [
2222                     {
2223                         tag: 'i',
2224                         cls : 'roo-collapse-toggle fa fa-chevron-down float-right ' + (this.collapsed ? 'collapsed' : '')
2225                     }
2226                    
2227                 ]
2228             };
2229             hdr.cn.push(hdr_ctr);
2230         }
2231         
2232         hdr_ctr.cn.push(        {
2233             tag: 'span',
2234             cls: 'roo-card-header-ctr' + ( this.header.length ? '' : ' d-none'),
2235             html : this.header
2236         });
2237         
2238         
2239         if (this.header_image.length) {
2240             cfg.cn.push({
2241                 tag : 'img',
2242                 cls : 'card-img-top',
2243                 src: this.header_image // escape?
2244             });
2245         } else {
2246             cfg.cn.push({
2247                     tag : 'div',
2248                     cls : 'card-img-top d-none' 
2249                 });
2250         }
2251             
2252         var body = {
2253             tag : 'div',
2254             cls : 'card-body' + (this.html === false  ? ' d-none' : ''),
2255             cn : []
2256         };
2257         var obody = body;
2258         if (this.collapsable || this.rotateable) {
2259             obody = {
2260                 tag: 'div',
2261                 cls : 'roo-collapsable collapse ' + (this.collapsed || this.rotated ? '' : 'show'),
2262                 cn : [  body ]
2263             };
2264         }
2265         
2266         cfg.cn.push(obody);
2267         
2268         if (this.title.length) {
2269             body.cn.push({
2270                 tag : 'div',
2271                 cls : 'card-title',
2272                 src: this.title // escape?
2273             });
2274         }  
2275         
2276         if (this.subtitle.length) {
2277             body.cn.push({
2278                 tag : 'div',
2279                 cls : 'card-title',
2280                 src: this.subtitle // escape?
2281             });
2282         }
2283         
2284         body.cn.push({
2285             tag : 'div',
2286             cls : 'roo-card-body-ctr'
2287         });
2288         
2289         if (this.html.length) {
2290             body.cn.push({
2291                 tag: 'div',
2292                 html : this.html
2293             });
2294         }
2295         // fixme ? handle objects?
2296         
2297         if (this.footer.length) {
2298            
2299             cfg.cn.push({
2300                 cls : 'card-footer ' + (this.rotated ? 'd-none' : ''),
2301                 html : this.footer
2302             });
2303             
2304         } else {
2305             cfg.cn.push({cls : 'card-footer d-none'});
2306         }
2307         
2308         // footer...
2309         
2310         return cfg;
2311     },
2312     
2313     
2314     getCardHeader : function()
2315     {
2316         var  ret = this.el.select('.card-header',true).first();
2317         if (ret.hasClass('d-none')) {
2318             ret.removeClass('d-none');
2319         }
2320         
2321         return ret;
2322     },
2323     getCardFooter : function()
2324     {
2325         var  ret = this.el.select('.card-footer',true).first();
2326         if (ret.hasClass('d-none')) {
2327             ret.removeClass('d-none');
2328         }
2329         
2330         return ret;
2331     },
2332     getCardImageTop : function()
2333     {
2334         var  ret = this.header_imageEl;
2335         if (ret.hasClass('d-none')) {
2336             ret.removeClass('d-none');
2337         }
2338             
2339         return ret;
2340     },
2341     
2342     getChildContainer : function()
2343     {
2344         
2345         if(!this.el){
2346             return false;
2347         }
2348         return this.el.select('.roo-card-body-ctr',true).first();    
2349     },
2350     
2351     initEvents: function() 
2352     {
2353         this.bodyEl = this.el.select('.card-body',true).first(); 
2354         this.containerEl = this.getChildContainer();
2355         if(this.dragable){
2356             this.dragZone = new Roo.dd.DragZone(this.getEl(), {
2357                     containerScroll: true,
2358                     ddGroup: this.drag_group || 'default_card_drag_group'
2359             });
2360             this.dragZone.getDragData = this.getDragData.createDelegate(this);
2361         }
2362         if (this.dropable) {
2363             this.dropZone = new Roo.dd.DropZone(this.el.select('.card-body',true).first() , {
2364                 containerScroll: true,
2365                 ddGroup: this.drop_group || 'default_card_drag_group'
2366             });
2367             this.dropZone.getTargetFromEvent = this.getTargetFromEvent.createDelegate(this);
2368             this.dropZone.onNodeEnter = this.onNodeEnter.createDelegate(this);
2369             this.dropZone.onNodeOver = this.onNodeOver.createDelegate(this);
2370             this.dropZone.onNodeOut = this.onNodeOut.createDelegate(this);
2371             this.dropZone.onNodeDrop = this.onNodeDrop.createDelegate(this);
2372         }
2373         
2374         if (this.collapsable) {
2375             this.el.select('.card-header',true).on('click', this.onToggleCollapse, this);
2376         }
2377         if (this.rotateable) {
2378             this.el.select('.card-header',true).on('click', this.onToggleRotate, this);
2379         }
2380         this.collapsableEl = this.el.select('.roo-collapsable',true).first();
2381          
2382         this.footerEl = this.el.select('.card-footer',true).first();
2383         this.collapsableToggleEl = this.el.select('.roo-collapse-toggle',true).first();
2384         this.headerContainerEl = this.el.select('.roo-card-header-ctr',true).first();
2385         this.headerEl = this.el.select('.card-header',true).first();
2386         
2387         if (this.rotated) {
2388             this.el.addClass('roo-card-rotated');
2389             this.fireEvent('rotate', this, true);
2390         }
2391         this.header_imageEl = this.el.select('.card-img-top',true).first(); 
2392         this.header_imageEl.on('load', this.onHeaderImageLoad, this );
2393         
2394     },
2395     getDragData : function(e)
2396     {
2397         var target = this.getEl();
2398         if (target) {
2399             //this.handleSelection(e);
2400             
2401             var dragData = {
2402                 source: this,
2403                 copy: false,
2404                 nodes: this.getEl(),
2405                 records: []
2406             };
2407             
2408             
2409             dragData.ddel = target.dom ;    // the div element
2410             Roo.log(target.getWidth( ));
2411             dragData.ddel.style.width = target.getWidth() + 'px';
2412             
2413             return dragData;
2414         }
2415         return false;
2416     },
2417     /**
2418     *    Part of the Roo.dd.DropZone interface. If no target node is found, the
2419     *    whole Element becomes the target, and this causes the drop gesture to append.
2420     *
2421     *    Returns an object:
2422     *     {
2423            
2424            position : 'below' or 'above'
2425            card  : relateive to card OBJECT (or true for no cards listed)
2426            items_n : relative to nth item in list
2427            card_n : relative to  nth card in list
2428     }
2429     *
2430     *    
2431     */
2432     getTargetFromEvent : function(e, dragged_card_el)
2433     {
2434         var target = e.getTarget();
2435         while ((target !== null) && (target.parentNode != this.containerEl.dom)) {
2436             target = target.parentNode;
2437         }
2438         
2439         var ret = {
2440             position: '',
2441             cards : [],
2442             card_n : -1,
2443             items_n : -1,
2444             card : false 
2445         };
2446         
2447         //Roo.log([ 'target' , target ? target.id : '--nothing--']);
2448         // see if target is one of the 'cards'...
2449         
2450         
2451         //Roo.log(this.items.length);
2452         var pos = false;
2453         
2454         var last_card_n = 0;
2455         var cards_len  = 0;
2456         for (var i = 0;i< this.items.length;i++) {
2457             
2458             if (!this.items[i].el.hasClass('card')) {
2459                  continue;
2460             }
2461             pos = this.getDropPoint(e, this.items[i].el.dom);
2462             
2463             cards_len = ret.cards.length;
2464             //Roo.log(this.items[i].el.dom.id);
2465             ret.cards.push(this.items[i]);
2466             last_card_n  = i;
2467             if (ret.card_n < 0 && pos == 'above') {
2468                 ret.position = cards_len > 0 ? 'below' : pos;
2469                 ret.items_n = i > 0 ? i - 1 : 0;
2470                 ret.card_n  = cards_len  > 0 ? cards_len - 1 : 0;
2471                 ret.card = ret.cards[ret.card_n];
2472             }
2473         }
2474         if (!ret.cards.length) {
2475             ret.card = true;
2476             ret.position = 'below';
2477             ret.items_n;
2478             return ret;
2479         }
2480         // could not find a card.. stick it at the end..
2481         if (ret.card_n < 0) {
2482             ret.card_n = last_card_n;
2483             ret.card = ret.cards[last_card_n];
2484             ret.items_n = this.items.indexOf(ret.cards[last_card_n]);
2485             ret.position = 'below';
2486         }
2487         
2488         if (this.items[ret.items_n].el == dragged_card_el) {
2489             return false;
2490         }
2491         
2492         if (ret.position == 'below') {
2493             var card_after = ret.card_n+1 == ret.cards.length ? false : ret.cards[ret.card_n+1];
2494             
2495             if (card_after  && card_after.el == dragged_card_el) {
2496                 return false;
2497             }
2498             return ret;
2499         }
2500         
2501         // its's after ..
2502         var card_before = ret.card_n > 0 ? ret.cards[ret.card_n-1] : false;
2503         
2504         if (card_before  && card_before.el == dragged_card_el) {
2505             return false;
2506         }
2507         
2508         return ret;
2509     },
2510     
2511     onNodeEnter : function(n, dd, e, data){
2512         return false;
2513     },
2514     onNodeOver : function(n, dd, e, data)
2515     {
2516        
2517         var target_info = this.getTargetFromEvent(e,data.source.el);
2518         if (target_info === false) {
2519             this.dropPlaceHolder('hide');
2520             return false;
2521         }
2522         Roo.log(['getTargetFromEvent', target_info ]);
2523         
2524         
2525         if (this.fireEvent('cardover', this, [ data ]) === false) {
2526             return false;
2527         }
2528         
2529         this.dropPlaceHolder('show', target_info,data);
2530         
2531         return false; 
2532     },
2533     onNodeOut : function(n, dd, e, data){
2534         this.dropPlaceHolder('hide');
2535      
2536     },
2537     onNodeDrop : function(n, dd, e, data)
2538     {
2539         
2540         // call drop - return false if
2541         
2542         // this could actually fail - if the Network drops..
2543         // we will ignore this at present..- client should probably reload
2544         // the whole set of cards if stuff like that fails.
2545         
2546         
2547         var info = this.getTargetFromEvent(e,data.source.el);
2548         if (info === false) {
2549             return false;
2550         }
2551         this.dropPlaceHolder('hide');
2552   
2553           
2554     
2555         this.acceptCard(data.source, info.position, info.card, info.items_n);
2556         return true;
2557          
2558     },
2559     firstChildCard : function()
2560     {
2561         for (var i = 0;i< this.items.length;i++) {
2562             
2563             if (!this.items[i].el.hasClass('card')) {
2564                  continue;
2565             }
2566             return this.items[i];
2567         }
2568         return this.items.length ? this.items[this.items.length-1] : false; // don't try and put stuff after the cards...
2569     },
2570     /**
2571      * accept card
2572      *
2573      * -        card.acceptCard(move_card, info.position, info.card, info.items_n);
2574      */
2575     acceptCard : function(move_card,  position, next_to_card )
2576     {
2577         if (this.fireEvent("drop", this, move_card, position, next_to_card) === false) {
2578             return false;
2579         }
2580         
2581         var to_items_n = next_to_card ? this.items.indexOf(next_to_card) : 0;
2582         
2583         move_card.parent().removeCard(move_card);
2584         
2585         
2586         var dom = move_card.el.dom;
2587         dom.style.width = ''; // clear with - which is set by drag.
2588         
2589         if (next_to_card !== false && next_to_card !== true && next_to_card.el.dom.parentNode) {
2590             var cardel = next_to_card.el.dom;
2591             
2592             if (position == 'above' ) {
2593                 cardel.parentNode.insertBefore(dom, cardel);
2594             } else if (cardel.nextSibling) {
2595                 cardel.parentNode.insertBefore(dom,cardel.nextSibling);
2596             } else {
2597                 cardel.parentNode.append(dom);
2598             }
2599         } else {
2600             // card container???
2601             this.containerEl.dom.append(dom);
2602         }
2603         
2604         //FIXME HANDLE card = true 
2605         
2606         // add this to the correct place in items.
2607         
2608         // remove Card from items.
2609         
2610        
2611         if (this.items.length) {
2612             var nitems = [];
2613             //Roo.log([info.items_n, info.position, this.items.length]);
2614             for (var i =0; i < this.items.length; i++) {
2615                 if (i == to_items_n && position == 'above') {
2616                     nitems.push(move_card);
2617                 }
2618                 nitems.push(this.items[i]);
2619                 if (i == to_items_n && position == 'below') {
2620                     nitems.push(move_card);
2621                 }
2622             }
2623             this.items = nitems;
2624             Roo.log(this.items);
2625         } else {
2626             this.items.push(move_card);
2627         }
2628         
2629         move_card.parentId = this.id;
2630         
2631         return true;
2632         
2633         
2634     },
2635     removeCard : function(c)
2636     {
2637         this.items = this.items.filter(function(e) { return e != c });
2638  
2639         var dom = c.el.dom;
2640         dom.parentNode.removeChild(dom);
2641         dom.style.width = ''; // clear with - which is set by drag.
2642         c.parentId = false;
2643         
2644     },
2645     
2646     /**    Decide whether to drop above or below a View node. */
2647     getDropPoint : function(e, n, dd)
2648     {
2649         if (dd) {
2650              return false;
2651         }
2652         if (n == this.containerEl.dom) {
2653             return "above";
2654         }
2655         var t = Roo.lib.Dom.getY(n), b = t + n.offsetHeight;
2656         var c = t + (b - t) / 2;
2657         var y = Roo.lib.Event.getPageY(e);
2658         if(y <= c) {
2659             return "above";
2660         }else{
2661             return "below";
2662         }
2663     },
2664     onToggleCollapse : function(e)
2665         {
2666         if (this.collapsed) {
2667             this.el.select('.roo-collapse-toggle').removeClass('collapsed');
2668             this.collapsableEl.addClass('show');
2669             this.collapsed = false;
2670             return;
2671         }
2672         this.el.select('.roo-collapse-toggle').addClass('collapsed');
2673         this.collapsableEl.removeClass('show');
2674         this.collapsed = true;
2675         
2676     
2677     },
2678     
2679     onToggleRotate : function(e)
2680     {
2681         this.collapsableEl.removeClass('show');
2682         this.footerEl.removeClass('d-none');
2683         this.el.removeClass('roo-card-rotated');
2684         this.el.removeClass('d-none');
2685         if (this.rotated) {
2686             
2687             this.collapsableEl.addClass('show');
2688             this.rotated = false;
2689             this.fireEvent('rotate', this, this.rotated);
2690             return;
2691         }
2692         this.el.addClass('roo-card-rotated');
2693         this.footerEl.addClass('d-none');
2694         this.el.select('.roo-collapsable').removeClass('show');
2695         
2696         this.rotated = true;
2697         this.fireEvent('rotate', this, this.rotated);
2698     
2699     },
2700     
2701     dropPlaceHolder: function (action, info, data)
2702     {
2703         if (this.dropEl === false) {
2704             this.dropEl = Roo.DomHelper.append(this.containerEl, {
2705             cls : 'd-none'
2706             },true);
2707         }
2708         this.dropEl.removeClass(['d-none', 'd-block']);        
2709         if (action == 'hide') {
2710             
2711             this.dropEl.addClass('d-none');
2712             return;
2713         }
2714         // FIXME - info.card == true!!!
2715         this.dropEl.dom.parentNode.removeChild(this.dropEl.dom);
2716         
2717         if (info.card !== true) {
2718             var cardel = info.card.el.dom;
2719             
2720             if (info.position == 'above') {
2721                 cardel.parentNode.insertBefore(this.dropEl.dom, cardel);
2722             } else if (cardel.nextSibling) {
2723                 cardel.parentNode.insertBefore(this.dropEl.dom,cardel.nextSibling);
2724             } else {
2725                 cardel.parentNode.append(this.dropEl.dom);
2726             }
2727         } else {
2728             // card container???
2729             this.containerEl.dom.append(this.dropEl.dom);
2730         }
2731         
2732         this.dropEl.addClass('d-block roo-card-dropzone');
2733         
2734         this.dropEl.setHeight( Roo.get(data.ddel).getHeight() );
2735         
2736         
2737     
2738     
2739     
2740     },
2741     setHeaderText: function(html)
2742     {
2743         this.header = html;
2744         if (this.headerContainerEl) {
2745             this.headerContainerEl.dom.innerHTML = html;
2746         }
2747     },
2748     onHeaderImageLoad : function(ev, he)
2749     {
2750         if (!this.header_image_fit_square) {
2751             return;
2752         }
2753         
2754         var hw = he.naturalHeight / he.naturalWidth;
2755         // wide image = < 0
2756         // tall image = > 1
2757         //var w = he.dom.naturalWidth;
2758         var ww = he.width;
2759         he.style.left =  0;
2760         he.style.position =  'relative';
2761         if (hw > 1) {
2762             var nw = (ww * (1/hw));
2763             Roo.get(he).setSize( ww * (1/hw),  ww);
2764             he.style.left =  ((ww - nw)/ 2) + 'px';
2765             he.style.position =  'relative';
2766         }
2767
2768     }
2769
2770     
2771 });
2772
2773 /*
2774  * - LGPL
2775  *
2776  * Card header - holder for the card header elements.
2777  * 
2778  */
2779
2780 /**
2781  * @class Roo.bootstrap.CardHeader
2782  * @extends Roo.bootstrap.Element
2783  * @parent Roo.bootstrap.Card
2784  * @children Roo.bootstrap.Component
2785  * Bootstrap CardHeader class
2786  * @constructor
2787  * Create a new Card Header - that you can embed children into
2788  * @param {Object} config The config object
2789  */
2790
2791 Roo.bootstrap.CardHeader = function(config){
2792     Roo.bootstrap.CardHeader.superclass.constructor.call(this, config);
2793 };
2794
2795 Roo.extend(Roo.bootstrap.CardHeader, Roo.bootstrap.Element,  {
2796     
2797     
2798     container_method : 'getCardHeader' 
2799     
2800      
2801     
2802     
2803    
2804 });
2805
2806  
2807
2808  /*
2809  * - LGPL
2810  *
2811  * Card footer - holder for the card footer elements.
2812  * 
2813  */
2814
2815 /**
2816  * @class Roo.bootstrap.CardFooter
2817  * @extends Roo.bootstrap.Element
2818  * @parent Roo.bootstrap.Card
2819  * @children Roo.bootstrap.Component
2820  * Bootstrap CardFooter class
2821  * 
2822  * @constructor
2823  * Create a new Card Footer - that you can embed children into
2824  * @param {Object} config The config object
2825  */
2826
2827 Roo.bootstrap.CardFooter = function(config){
2828     Roo.bootstrap.CardFooter.superclass.constructor.call(this, config);
2829 };
2830
2831 Roo.extend(Roo.bootstrap.CardFooter, Roo.bootstrap.Element,  {
2832     
2833     
2834     container_method : 'getCardFooter' 
2835     
2836      
2837     
2838     
2839    
2840 });
2841
2842  
2843
2844  /*
2845  * - LGPL
2846  *
2847  * Card header - holder for the card header elements.
2848  * 
2849  */
2850
2851 /**
2852  * @class Roo.bootstrap.CardImageTop
2853  * @extends Roo.bootstrap.Element
2854  * @parent Roo.bootstrap.Card
2855  * @children Roo.bootstrap.Component
2856  * Bootstrap CardImageTop class
2857  * 
2858  * @constructor
2859  * Create a new Card Image Top container
2860  * @param {Object} config The config object
2861  */
2862
2863 Roo.bootstrap.CardImageTop = function(config){
2864     Roo.bootstrap.CardImageTop.superclass.constructor.call(this, config);
2865 };
2866
2867 Roo.extend(Roo.bootstrap.CardImageTop, Roo.bootstrap.Element,  {
2868     
2869    
2870     container_method : 'getCardImageTop' 
2871     
2872      
2873     
2874    
2875 });
2876
2877  
2878
2879  
2880 /*
2881 * Licence: LGPL
2882 */
2883
2884 /**
2885  * @class Roo.bootstrap.ButtonUploader
2886  * @extends Roo.bootstrap.Button
2887  * Bootstrap Button Uploader class - it's a button which when you add files to it
2888  *
2889  * 
2890  * @cfg {Number} errorTimeout default 3000
2891  * @cfg {Array}  images  an array of ?? Img objects ??? when loading existing files..
2892  * @cfg {Array}  html The button text.
2893  * @cfg {Boolean}  multiple (default true) Should the upload allow multiple files to be uploaded.
2894  *
2895  * @constructor
2896  * Create a new CardUploader
2897  * @param {Object} config The config object
2898  */
2899
2900 Roo.bootstrap.ButtonUploader = function(config){
2901     
2902  
2903     
2904     Roo.bootstrap.ButtonUploader.superclass.constructor.call(this, config);
2905     
2906      
2907      this.addEvents({
2908          // raw events
2909         /**
2910          * @event beforeselect
2911          * When button is pressed, before show upload files dialog is shown
2912          * @param {Roo.bootstrap.UploaderButton} this
2913          *
2914          */
2915         'beforeselect' : true,
2916          /**
2917          * @event fired when files have been selected, 
2918          * When a the download link is clicked
2919          * @param {Roo.bootstrap.UploaderButton} this
2920          * @param {Array} Array of files that have been uploaded
2921          */
2922         'uploaded' : true
2923         
2924     });
2925 };
2926  
2927 Roo.extend(Roo.bootstrap.ButtonUploader, Roo.bootstrap.Button,  {
2928     
2929      
2930     errorTimeout : 3000,
2931      
2932     images : false,
2933    
2934     fileCollection : false,
2935     allowBlank : true,
2936     
2937     multiple : true,
2938     
2939     getAutoCreate : function()
2940     {
2941        
2942         
2943         return  {
2944             cls :'div' ,
2945             cn : [
2946                 Roo.bootstrap.Button.prototype.getAutoCreate.call(this) 
2947             ]
2948         };
2949            
2950          
2951     },
2952      
2953    
2954     initEvents : function()
2955     {
2956         
2957         Roo.bootstrap.Button.prototype.initEvents.call(this);
2958         
2959         
2960         
2961         
2962         
2963         this.urlAPI = (window.createObjectURL && window) || 
2964                                 (window.URL && URL.revokeObjectURL && URL) || 
2965                                 (window.webkitURL && webkitURL);
2966                         
2967         var im = {
2968             tag: 'input',
2969             type : 'file',
2970             cls : 'd-none  roo-card-upload-selector' 
2971           
2972         };
2973         if (this.multiple) {
2974             im.multiple = 'multiple';
2975         }
2976         this.selectorEl = Roo.get(document.body).createChild(im); // so it does not capture click event for navitem.
2977        
2978         //this.selectorEl = this.el.select('.roo-card-upload-selector', true).first();
2979         
2980         this.selectorEl.on('change', this.onFileSelected, this);
2981          
2982          
2983        
2984     },
2985     
2986    
2987     onClick : function(e)
2988     {
2989         e.preventDefault();
2990         
2991         if ( this.fireEvent('beforeselect', this) === false) {
2992             return;
2993         }
2994          
2995         this.selectorEl.dom.click();
2996          
2997     },
2998     
2999     onFileSelected : function(e)
3000     {
3001         e.preventDefault();
3002         
3003         if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
3004             return;
3005         }
3006         var files = Array.prototype.slice.call(this.selectorEl.dom.files);
3007         this.selectorEl.dom.value  = '';// hopefully reset..
3008         
3009         this.fireEvent('uploaded', this,  files );
3010         
3011     },
3012     
3013        
3014    
3015     
3016     /**
3017      * addCard - add an Attachment to the uploader
3018      * @param data - the data about the image to upload
3019      *
3020      * {
3021           id : 123
3022           title : "Title of file",
3023           is_uploaded : false,
3024           src : "http://.....",
3025           srcfile : { the File upload object },
3026           mimetype : file.type,
3027           preview : false,
3028           is_deleted : 0
3029           .. any other data...
3030         }
3031      *
3032      * 
3033     */
3034      
3035     reset: function()
3036     {
3037          
3038          this.selectorEl
3039     } 
3040     
3041     
3042     
3043     
3044 });
3045  /*
3046  * - LGPL
3047  *
3048  * image
3049  * 
3050  */
3051
3052
3053 /**
3054  * @class Roo.bootstrap.Img
3055  * @extends Roo.bootstrap.Component
3056  * Bootstrap Img class
3057  * @cfg {Boolean} imgResponsive false | true
3058  * @cfg {String} border rounded | circle | thumbnail
3059  * @cfg {String} src image source
3060  * @cfg {String} alt image alternative text
3061  * @cfg {String} href a tag href
3062  * @cfg {String} target (_self|_blank|_parent|_top)target for a href.
3063  * @cfg {String} xsUrl xs image source
3064  * @cfg {String} smUrl sm image source
3065  * @cfg {String} mdUrl md image source
3066  * @cfg {String} lgUrl lg image source
3067  * @cfg {Boolean} backgroundContain (use style background and contain image in content)
3068  * 
3069  * @constructor
3070  * Create a new Input
3071  * @param {Object} config The config object
3072  */
3073
3074 Roo.bootstrap.Img = function(config){
3075     Roo.bootstrap.Img.superclass.constructor.call(this, config);
3076     
3077     this.addEvents({
3078         // img events
3079         /**
3080          * @event click
3081          * The img click event for the img.
3082          * @param {Roo.EventObject} e
3083          */
3084         "click" : true,
3085         /**
3086          * @event load
3087          * The when any image loads
3088          * @param {Roo.EventObject} e
3089          */
3090         "load" : true
3091     });
3092 };
3093
3094 Roo.extend(Roo.bootstrap.Img, Roo.bootstrap.Component,  {
3095     
3096     imgResponsive: true,
3097     border: '',
3098     src: 'data:image/gif;base64,R0lGODlhAQABAAD/ACwAAAAAAQABAAACADs=',
3099     href: false,
3100     target: false,
3101     xsUrl: '',
3102     smUrl: '',
3103     mdUrl: '',
3104     lgUrl: '',
3105     backgroundContain : false,
3106
3107     getAutoCreate : function()
3108     {   
3109         if(this.src || (!this.xsUrl && !this.smUrl && !this.mdUrl && !this.lgUrl)){
3110             return this.createSingleImg();
3111         }
3112         
3113         var cfg = {
3114             tag: 'div',
3115             cls: 'roo-image-responsive-group',
3116             cn: []
3117         };
3118         var _this = this;
3119         
3120         Roo.each(['xs', 'sm', 'md', 'lg'], function(size){
3121             
3122             if(!_this[size + 'Url']){
3123                 return;
3124             }
3125             
3126             var img = {
3127                 tag: 'img',
3128                 cls: (_this.imgResponsive) ? 'img-responsive' : '',
3129                 html: _this.html || cfg.html,
3130                 src: _this[size + 'Url']
3131             };
3132             
3133             img.cls += ' roo-image-responsive-' + size;
3134             
3135             var s = ['xs', 'sm', 'md', 'lg'];
3136             
3137             s.splice(s.indexOf(size), 1);
3138             
3139             Roo.each(s, function(ss){
3140                 img.cls += ' hidden-' + ss;
3141             });
3142             
3143             if (['rounded','circle','thumbnail'].indexOf(_this.border)>-1) {
3144                 cfg.cls += ' img-' + _this.border;
3145             }
3146             
3147             if(_this.alt){
3148                 cfg.alt = _this.alt;
3149             }
3150             
3151             if(_this.href){
3152                 var a = {
3153                     tag: 'a',
3154                     href: _this.href,
3155                     cn: [
3156                         img
3157                     ]
3158                 };
3159
3160                 if(this.target){
3161                     a.target = _this.target;
3162                 }
3163             }
3164             
3165             cfg.cn.push((_this.href) ? a : img);
3166             
3167         });
3168         
3169         return cfg;
3170     },
3171     
3172     createSingleImg : function()
3173     {
3174         var cfg = {
3175             tag: 'img',
3176             cls: (this.imgResponsive) ? 'img-responsive' : '',
3177             html : null,
3178             src : Roo.BLANK_IMAGE_URL  // just incase src get's set to undefined?!?
3179         };
3180         
3181         if (this.backgroundContain) {
3182             cfg.cls += ' background-contain';
3183         }
3184         
3185         cfg.html = this.html || cfg.html;
3186         
3187         if (this.backgroundContain) {
3188             cfg.style="background-image: url(" + this.src + ')';
3189         } else {
3190             cfg.src = this.src || cfg.src;
3191         }
3192         
3193         if (['rounded','circle','thumbnail'].indexOf(this.border)>-1) {
3194             cfg.cls += ' img-' + this.border;
3195         }
3196         
3197         if(this.alt){
3198             cfg.alt = this.alt;
3199         }
3200         
3201         if(this.href){
3202             var a = {
3203                 tag: 'a',
3204                 href: this.href,
3205                 cn: [
3206                     cfg
3207                 ]
3208             };
3209             
3210             if(this.target){
3211                 a.target = this.target;
3212             }
3213             
3214         }
3215         
3216         return (this.href) ? a : cfg;
3217     },
3218     
3219     initEvents: function() 
3220     {
3221         if(!this.href){
3222             this.el.on('click', this.onClick, this);
3223         }
3224         if(this.src || (!this.xsUrl && !this.smUrl && !this.mdUrl && !this.lgUrl)){
3225             this.el.on('load', this.onImageLoad, this);
3226         } else {
3227             // not sure if this works.. not tested
3228             this.el.select('img', true).on('load', this.onImageLoad, this);
3229         }
3230         
3231     },
3232     
3233     onClick : function(e)
3234     {
3235         Roo.log('img onclick');
3236         this.fireEvent('click', this, e);
3237     },
3238     onImageLoad: function(e)
3239     {
3240         Roo.log('img load');
3241         this.fireEvent('load', this, e);
3242     },
3243     
3244     /**
3245      * Sets the url of the image - used to update it
3246      * @param {String} url the url of the image
3247      */
3248     
3249     setSrc : function(url)
3250     {
3251         this.src =  url;
3252         
3253         if(this.src || (!this.xsUrl && !this.smUrl && !this.mdUrl && !this.lgUrl)){
3254             if (this.backgroundContain) {
3255                 this.el.dom.style.backgroundImage =  'url(' + url + ')';
3256             } else {
3257                 this.el.dom.src =  url;
3258             }
3259             return;
3260         }
3261         
3262         this.el.select('img', true).first().dom.src =  url;
3263     }
3264     
3265     
3266    
3267 });
3268
3269  /*
3270  * - LGPL
3271  *
3272  * image
3273  * 
3274  */
3275
3276
3277 /**
3278  * @class Roo.bootstrap.Link
3279  * @extends Roo.bootstrap.Component
3280  * @children Roo.bootstrap.Component
3281  * Bootstrap Link Class (eg. '<a href>')
3282  
3283  * @cfg {String} alt image alternative text
3284  * @cfg {String} href a tag href
3285  * @cfg {String} target (_self|_blank|_parent|_top) target for a href.
3286  * @cfg {String} html the content of the link.
3287  * @cfg {String} anchor name for the anchor link
3288  * @cfg {String} fa - favicon
3289
3290  * @cfg {Boolean} preventDefault (true | false) default false
3291
3292  * 
3293  * @constructor
3294  * Create a new Input
3295  * @param {Object} config The config object
3296  */
3297
3298 Roo.bootstrap.Link = function(config){
3299     Roo.bootstrap.Link.superclass.constructor.call(this, config);
3300     
3301     this.addEvents({
3302         // img events
3303         /**
3304          * @event click
3305          * The img click event for the img.
3306          * @param {Roo.EventObject} e
3307          */
3308         "click" : true
3309     });
3310 };
3311
3312 Roo.extend(Roo.bootstrap.Link, Roo.bootstrap.Component,  {
3313     
3314     href: false,
3315     target: false,
3316     preventDefault: false,
3317     anchor : false,
3318     alt : false,
3319     fa: false,
3320
3321
3322     getAutoCreate : function()
3323     {
3324         var html = this.html || '';
3325         
3326         if (this.fa !== false) {
3327             html = '<i class="fa fa-' + this.fa + '"></i>';
3328         }
3329         var cfg = {
3330             tag: 'a'
3331         };
3332         // anchor's do not require html/href...
3333         if (this.anchor === false) {
3334             cfg.html = html;
3335             cfg.href = this.href || '#';
3336         } else {
3337             cfg.name = this.anchor;
3338             if (this.html !== false || this.fa !== false) {
3339                 cfg.html = html;
3340             }
3341             if (this.href !== false) {
3342                 cfg.href = this.href;
3343             }
3344         }
3345         
3346         if(this.alt !== false){
3347             cfg.alt = this.alt;
3348         }
3349         
3350         
3351         if(this.target !== false) {
3352             cfg.target = this.target;
3353         }
3354         
3355         return cfg;
3356     },
3357     
3358     initEvents: function() {
3359         
3360         if(!this.href || this.preventDefault){
3361             this.el.on('click', this.onClick, this);
3362         }
3363     },
3364     
3365     onClick : function(e)
3366     {
3367         if(this.preventDefault){
3368             e.preventDefault();
3369         }
3370         //Roo.log('img onclick');
3371         this.fireEvent('click', this, e);
3372     }
3373    
3374 });
3375
3376  /*
3377  * - LGPL
3378  *
3379  * header
3380  * 
3381  */
3382
3383 /**
3384  * @class Roo.bootstrap.Header
3385  * @extends Roo.bootstrap.Component
3386  * @children Roo.bootstrap.Component
3387  * Bootstrap Header class
3388  *
3389  * 
3390  * @cfg {String} html content of header
3391  * @cfg {Number} level (1|2|3|4|5|6) default 1
3392  * 
3393  * @constructor
3394  * Create a new Header
3395  * @param {Object} config The config object
3396  */
3397
3398
3399 Roo.bootstrap.Header  = function(config){
3400     Roo.bootstrap.Header.superclass.constructor.call(this, config);
3401 };
3402
3403 Roo.extend(Roo.bootstrap.Header, Roo.bootstrap.Component,  {
3404     
3405     //href : false,
3406     html : false,
3407     level : 1,
3408     
3409     
3410     
3411     getAutoCreate : function(){
3412         
3413         
3414         
3415         var cfg = {
3416             tag: 'h' + (1 *this.level),
3417             html: this.html || ''
3418         } ;
3419         
3420         return cfg;
3421     }
3422    
3423 });
3424
3425  
3426
3427  /**
3428  * @class Roo.bootstrap.MenuMgr
3429  * @licence LGPL
3430  * Provides a common registry of all menu items on a page so that they can be easily accessed by id.
3431  * @static
3432  */
3433 Roo.bootstrap.menu.Manager = function(){
3434    var menus, active, groups = {}, attached = false, lastShow = new Date();
3435
3436    // private - called when first menu is created
3437    function init(){
3438        menus = {};
3439        active = new Roo.util.MixedCollection();
3440        Roo.get(document).addKeyListener(27, function(){
3441            if(active.length > 0){
3442                hideAll();
3443            }
3444        });
3445    }
3446
3447    // private
3448    function hideAll(){
3449        if(active && active.length > 0){
3450            var c = active.clone();
3451            c.each(function(m){
3452                m.hide();
3453            });
3454        }
3455    }
3456
3457    // private
3458    function onHide(m){
3459        active.remove(m);
3460        if(active.length < 1){
3461            Roo.get(document).un("mouseup", onMouseDown);
3462             
3463            attached = false;
3464        }
3465    }
3466
3467    // private
3468    function onShow(m){
3469        var last = active.last();
3470        lastShow = new Date();
3471        active.add(m);
3472        if(!attached){
3473           Roo.get(document).on("mouseup", onMouseDown);
3474            
3475            attached = true;
3476        }
3477        if(m.parentMenu){
3478           //m.getEl().setZIndex(parseInt(m.parentMenu.getEl().getStyle("z-index"), 10) + 3);
3479           m.parentMenu.activeChild = m;
3480        }else if(last && last.isVisible()){
3481           //m.getEl().setZIndex(parseInt(last.getEl().getStyle("z-index"), 10) + 3);
3482        }
3483    }
3484
3485    // private
3486    function onBeforeHide(m){
3487        if(m.activeChild){
3488            m.activeChild.hide();
3489        }
3490        if(m.autoHideTimer){
3491            clearTimeout(m.autoHideTimer);
3492            delete m.autoHideTimer;
3493        }
3494    }
3495
3496    // private
3497    function onBeforeShow(m){
3498        var pm = m.parentMenu;
3499        if(!pm && !m.allowOtherMenus){
3500            hideAll();
3501        }else if(pm && pm.activeChild && active != m){
3502            pm.activeChild.hide();
3503        }
3504    }
3505
3506    // private this should really trigger on mouseup..
3507    function onMouseDown(e){
3508         Roo.log("on Mouse Up");
3509         
3510         if(lastShow.getElapsed() > 50 && active.length > 0 && !e.getTarget(".dropdown-menu") && !e.getTarget('.user-menu')){
3511             Roo.log("MenuManager hideAll");
3512             hideAll();
3513             e.stopEvent();
3514         }
3515         
3516         
3517    }
3518
3519    // private
3520    function onBeforeCheck(mi, state){
3521        if(state){
3522            var g = groups[mi.group];
3523            for(var i = 0, l = g.length; i < l; i++){
3524                if(g[i] != mi){
3525                    g[i].setChecked(false);
3526                }
3527            }
3528        }
3529    }
3530
3531    return {
3532
3533        /**
3534         * Hides all menus that are currently visible
3535         */
3536        hideAll : function(){
3537             hideAll();  
3538        },
3539
3540        // private
3541        register : function(menu){
3542            if(!menus){
3543                init();
3544            }
3545            menus[menu.id] = menu;
3546            menu.on("beforehide", onBeforeHide);
3547            menu.on("hide", onHide);
3548            menu.on("beforeshow", onBeforeShow);
3549            menu.on("show", onShow);
3550            var g = menu.group;
3551            if(g && menu.events["checkchange"]){
3552                if(!groups[g]){
3553                    groups[g] = [];
3554                }
3555                groups[g].push(menu);
3556                menu.on("checkchange", onCheck);
3557            }
3558        },
3559
3560         /**
3561          * Returns a {@link Roo.menu.Menu} object
3562          * @param {String/Object} menu The string menu id, an existing menu object reference, or a Menu config that will
3563          * be used to generate and return a new Menu instance.
3564          */
3565        get : function(menu){
3566            if(typeof menu == "string"){ // menu id
3567                return menus[menu];
3568            }else if(menu.events){  // menu instance
3569                return menu;
3570            }
3571            /*else if(typeof menu.length == 'number'){ // array of menu items?
3572                return new Roo.bootstrap.Menu({items:menu});
3573            }else{ // otherwise, must be a config
3574                return new Roo.bootstrap.Menu(menu);
3575            }
3576            */
3577            return false;
3578        },
3579
3580        // private
3581        unregister : function(menu){
3582            delete menus[menu.id];
3583            menu.un("beforehide", onBeforeHide);
3584            menu.un("hide", onHide);
3585            menu.un("beforeshow", onBeforeShow);
3586            menu.un("show", onShow);
3587            var g = menu.group;
3588            if(g && menu.events["checkchange"]){
3589                groups[g].remove(menu);
3590                menu.un("checkchange", onCheck);
3591            }
3592        },
3593
3594        // private
3595        registerCheckable : function(menuItem){
3596            var g = menuItem.group;
3597            if(g){
3598                if(!groups[g]){
3599                    groups[g] = [];
3600                }
3601                groups[g].push(menuItem);
3602                menuItem.on("beforecheckchange", onBeforeCheck);
3603            }
3604        },
3605
3606        // private
3607        unregisterCheckable : function(menuItem){
3608            var g = menuItem.group;
3609            if(g){
3610                groups[g].remove(menuItem);
3611                menuItem.un("beforecheckchange", onBeforeCheck);
3612            }
3613        }
3614    };
3615 }(); 
3616 /**
3617  * @class Roo.bootstrap.menu.Menu
3618  * @extends Roo.bootstrap.Component
3619  * @licence LGPL
3620  * @children Roo.bootstrap.menu.Item Roo.bootstrap.menu.Separator
3621  * @parent none
3622  * Bootstrap Menu class - container for MenuItems - normally has to be added to a object that supports the menu property
3623  * 
3624  * @cfg {String} type (dropdown|treeview|submenu) type of menu
3625  * @cfg {bool} hidden  if the menu should be hidden when rendered.
3626  * @cfg {bool} stopEvent (true|false)  Stop event after trigger press (default true)
3627  * @cfg {bool} isLink (true|false)  the menu has link disable auto expand and collaspe (default false)
3628 * @cfg {bool} hideTrigger (true|false)  default false - hide the carret for trigger.
3629 * @cfg {String} align  default tl-bl? == below  - how the menu should be aligned. 
3630  
3631  * @constructor
3632  * Create a new Menu
3633  * @param {Object} config The config objectQ
3634  */
3635
3636
3637 Roo.bootstrap.menu.Menu = function(config){
3638     
3639     if (config.type == 'treeview') {
3640         // normally menu's are drawn attached to the document to handle layering etc..
3641         // however treeview (used by the docs menu is drawn into the parent element)
3642         this.container_method = 'getChildContainer'; 
3643     }
3644     
3645     Roo.bootstrap.menu.Menu.superclass.constructor.call(this, config);
3646     if (this.registerMenu && this.type != 'treeview')  {
3647         Roo.bootstrap.menu.Manager.register(this);
3648     }
3649     
3650     
3651     this.addEvents({
3652         /**
3653          * @event beforeshow
3654          * Fires before this menu is displayed (return false to block)
3655          * @param {Roo.menu.Menu} this
3656          */
3657         beforeshow : true,
3658         /**
3659          * @event beforehide
3660          * Fires before this menu is hidden (return false to block)
3661          * @param {Roo.menu.Menu} this
3662          */
3663         beforehide : true,
3664         /**
3665          * @event show
3666          * Fires after this menu is displayed
3667          * @param {Roo.menu.Menu} this
3668          */
3669         show : true,
3670         /**
3671          * @event hide
3672          * Fires after this menu is hidden
3673          * @param {Roo.menu.Menu} this
3674          */
3675         hide : true,
3676         /**
3677          * @event click
3678          * Fires when this menu is clicked (or when the enter key is pressed while it is active)
3679          * @param {Roo.menu.Menu} this
3680          * @param {Roo.menu.Item} menuItem The menu item that was clicked
3681          * @param {Roo.EventObject} e
3682          */
3683         click : true,
3684         /**
3685          * @event mouseover
3686          * Fires when the mouse is hovering over this menu
3687          * @param {Roo.menu.Menu} this
3688          * @param {Roo.EventObject} e
3689          * @param {Roo.menu.Item} menuItem The menu item that was clicked
3690          */
3691         mouseover : true,
3692         /**
3693          * @event mouseout
3694          * Fires when the mouse exits this menu
3695          * @param {Roo.menu.Menu} this
3696          * @param {Roo.EventObject} e
3697          * @param {Roo.menu.Item} menuItem The menu item that was clicked
3698          */
3699         mouseout : true,
3700         /**
3701          * @event itemclick
3702          * Fires when a menu item contained in this menu is clicked
3703          * @param {Roo.menu.BaseItem} baseItem The BaseItem that was clicked
3704          * @param {Roo.EventObject} e
3705          */
3706         itemclick: true
3707     });
3708     this.menuitems = new Roo.util.MixedCollection(false, function(o) { return o.el.id; });
3709 };
3710
3711 Roo.extend(Roo.bootstrap.menu.Menu, Roo.bootstrap.Component,  {
3712     
3713    /// html : false,
3714    
3715     triggerEl : false,  // is this set by component builder? -- it should really be fetched from parent()???
3716     type: false,
3717     /**
3718      * @cfg {Boolean} registerMenu True (default) - means that clicking on screen etc. hides it.
3719      */
3720     registerMenu : true,
3721     
3722     menuItems :false, // stores the menu items..
3723     
3724     hidden:true,
3725         
3726     parentMenu : false,
3727     
3728     stopEvent : true,
3729     
3730     isLink : false,
3731     
3732     container_method : 'getDocumentBody', // so the menu is rendered on the body and zIndex works.
3733     
3734     hideTrigger : false,
3735     
3736     align : 'tl-bl?',
3737     
3738     
3739     getChildContainer : function() {
3740         return this.el;  
3741     },
3742     
3743     getAutoCreate : function(){
3744          
3745         //if (['right'].indexOf(this.align)!==-1) {
3746         //    cfg.cn[1].cls += ' pull-right'
3747         //}
3748          
3749         var cfg = {
3750             tag : 'ul',
3751             cls : 'dropdown-menu shadow' ,
3752             style : 'z-index:1000'
3753             
3754         };
3755         
3756         if (this.type === 'submenu') {
3757             cfg.cls = 'submenu active';
3758         }
3759         if (this.type === 'treeview') {
3760             cfg.cls = 'treeview-menu';
3761         }
3762         
3763         return cfg;
3764     },
3765     initEvents : function() {
3766         
3767        // Roo.log("ADD event");
3768        // Roo.log(this.triggerEl.dom);
3769         if (this.triggerEl) {
3770             
3771             this.triggerEl.on('click', this.onTriggerClick, this);
3772             
3773             this.triggerEl.on(Roo.isTouch ? 'touchstart' : 'mouseup', this.onTriggerPress, this);
3774             
3775             if (!this.hideTrigger) {
3776                 if (this.triggerEl.hasClass('nav-item') && this.triggerEl.select('.nav-link',true).length) {
3777                     // dropdown toggle on the 'a' in BS4?
3778                     this.triggerEl.select('.nav-link',true).first().addClass('dropdown-toggle');
3779                 } else {
3780                     this.triggerEl.addClass('dropdown-toggle');
3781                 }
3782             }
3783         }
3784         
3785         if (Roo.isTouch) {
3786             this.el.on('touchstart'  , this.onTouch, this);
3787         }
3788         this.el.on('click' , this.onClick, this);
3789
3790         this.el.on("mouseover", this.onMouseOver, this);
3791         this.el.on("mouseout", this.onMouseOut, this);
3792         
3793     },
3794     
3795     findTargetItem : function(e)
3796     {
3797         var t = e.getTarget(".dropdown-menu-item", this.el,  true);
3798         if(!t){
3799             return false;
3800         }
3801         //Roo.log(t);         Roo.log(t.id);
3802         if(t && t.id){
3803             //Roo.log(this.menuitems);
3804             return this.menuitems.get(t.id);
3805             
3806             //return this.items.get(t.menuItemId);
3807         }
3808         
3809         return false;
3810     },
3811     
3812     onTouch : function(e) 
3813     {
3814         Roo.log("menu.onTouch");
3815         //e.stopEvent(); this make the user popdown broken
3816         this.onClick(e);
3817     },
3818     
3819     onClick : function(e)
3820     {
3821         Roo.log("menu.onClick");
3822         
3823         var t = this.findTargetItem(e);
3824         if(!t || t.isContainer){
3825             return;
3826         }
3827         Roo.log(e);
3828         /*
3829         if (Roo.isTouch && e.type == 'touchstart' && t.menu  && !t.disabled) {
3830             if(t == this.activeItem && t.shouldDeactivate(e)){
3831                 this.activeItem.deactivate();
3832                 delete this.activeItem;
3833                 return;
3834             }
3835             if(t.canActivate){
3836                 this.setActiveItem(t, true);
3837             }
3838             return;
3839             
3840             
3841         }
3842         */
3843        
3844         Roo.log('pass click event');
3845         
3846         t.onClick(e);
3847         
3848         this.fireEvent("click", this, t, e);
3849         
3850         var _this = this;
3851         
3852         if(!t.href.length || t.href == '#'){
3853             (function() { _this.hide(); }).defer(100);
3854         }
3855         
3856     },
3857     
3858     onMouseOver : function(e){
3859         var t  = this.findTargetItem(e);
3860         //Roo.log(t);
3861         //if(t){
3862         //    if(t.canActivate && !t.disabled){
3863         //        this.setActiveItem(t, true);
3864         //    }
3865         //}
3866         
3867         this.fireEvent("mouseover", this, e, t);
3868     },
3869     isVisible : function(){
3870         return !this.hidden;
3871     },
3872     onMouseOut : function(e){
3873         var t  = this.findTargetItem(e);
3874         
3875         //if(t ){
3876         //    if(t == this.activeItem && t.shouldDeactivate(e)){
3877         //        this.activeItem.deactivate();
3878         //        delete this.activeItem;
3879         //    }
3880         //}
3881         this.fireEvent("mouseout", this, e, t);
3882     },
3883     
3884     
3885     /**
3886      * Displays this menu relative to another element
3887      * @param {String/HTMLElement/Roo.Element} element The element to align to
3888      * @param {String} position (optional) The {@link Roo.Element#alignTo} anchor position to use in aligning to
3889      * the element (defaults to this.defaultAlign)
3890      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
3891      */
3892     show : function(el, pos, parentMenu)
3893     {
3894         if (false === this.fireEvent("beforeshow", this)) {
3895             Roo.log("show canceled");
3896             return;
3897         }
3898         this.parentMenu = parentMenu;
3899         if(!this.el){
3900             this.render();
3901         }
3902         this.el.addClass('show'); // show otherwise we do not know how big we are..
3903          
3904         var xy = this.el.getAlignToXY(el, pos);
3905         
3906         // bl-tl << left align  below
3907         // tl-bl << left align 
3908         
3909         if(this.el.getWidth() + xy[0] >= Roo.lib.Dom.getViewWidth()){
3910             // if it goes to far to the right.. -> align left.
3911             xy = this.el.getAlignToXY(el, this.align.replace('/l/g', 'r'))
3912         }
3913         if(xy[0] < 0){
3914             // was left align - go right?
3915             xy = this.el.getAlignToXY(el, this.align.replace('/r/g', 'l'))
3916         }
3917         
3918         // goes down the bottom
3919         if(this.el.getHeight() + xy[1] >= Roo.lib.Dom.getViewHeight() ||
3920            xy[1]  < 0 ){
3921             var a = this.align.replace('?', '').split('-');
3922             xy = this.el.getAlignToXY(el, a[1]  + '-' + a[0] + '?')
3923             
3924         }
3925         
3926         this.showAt(  xy , parentMenu, false);
3927     },
3928      /**
3929      * Displays this menu at a specific xy position
3930      * @param {Array} xyPosition Contains X & Y [x, y] values for the position at which to show the menu (coordinates are page-based)
3931      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
3932      */
3933     showAt : function(xy, parentMenu, /* private: */_e){
3934         this.parentMenu = parentMenu;
3935         if(!this.el){
3936             this.render();
3937         }
3938         if(_e !== false){
3939             this.fireEvent("beforeshow", this);
3940             //xy = this.el.adjustForConstraints(xy);
3941         }
3942         
3943         //this.el.show();
3944         this.hideMenuItems();
3945         this.hidden = false;
3946         if (this.triggerEl) {
3947             this.triggerEl.addClass('open');
3948         }
3949         
3950         this.el.addClass('show');
3951         
3952         
3953         
3954         // reassign x when hitting right
3955         
3956         // reassign y when hitting bottom
3957         
3958         // but the list may align on trigger left or trigger top... should it be a properity?
3959         
3960         if(this.el.getStyle('top') != 'auto' && this.el.getStyle('top').slice(-1) != "%"){
3961             this.el.setXY(xy);
3962         }
3963         
3964         this.focus();
3965         this.fireEvent("show", this);
3966     },
3967     
3968     focus : function(){
3969         return;
3970         if(!this.hidden){
3971             this.doFocus.defer(50, this);
3972         }
3973     },
3974
3975     doFocus : function(){
3976         if(!this.hidden){
3977             this.focusEl.focus();
3978         }
3979     },
3980
3981     /**
3982      * Hides this menu and optionally all parent menus
3983      * @param {Boolean} deep (optional) True to hide all parent menus recursively, if any (defaults to false)
3984      */
3985     hide : function(deep)
3986     {
3987         if (false === this.fireEvent("beforehide", this)) {
3988             Roo.log("hide canceled");
3989             return;
3990         }
3991         this.hideMenuItems();
3992         if(this.el && this.isVisible()){
3993            
3994             if(this.activeItem){
3995                 this.activeItem.deactivate();
3996                 this.activeItem = null;
3997             }
3998             if (this.triggerEl) {
3999                 this.triggerEl.removeClass('open');
4000             }
4001             
4002             this.el.removeClass('show');
4003             this.hidden = true;
4004             this.fireEvent("hide", this);
4005         }
4006         if(deep === true && this.parentMenu){
4007             this.parentMenu.hide(true);
4008         }
4009     },
4010     
4011     onTriggerClick : function(e)
4012     {
4013         Roo.log('trigger click');
4014         
4015         var target = e.getTarget();
4016         
4017         Roo.log(target.nodeName.toLowerCase());
4018         
4019         if(target.nodeName.toLowerCase() === 'i'){
4020             e.preventDefault();
4021         }
4022         
4023     },
4024     
4025     onTriggerPress  : function(e)
4026     {
4027         Roo.log('trigger press');
4028         //Roo.log(e.getTarget());
4029        // Roo.log(this.triggerEl.dom);
4030        
4031         // trigger only occurs on normal menu's -- if it's a treeview or dropdown... do not hide/show..
4032         var pel = Roo.get(e.getTarget());
4033         if (pel.findParent('.dropdown-menu') || pel.findParent('.treeview-menu') ) {
4034             Roo.log('is treeview or dropdown?');
4035             return;
4036         }
4037         
4038         if(e.getTarget().nodeName.toLowerCase() !== 'i' && this.isLink){
4039             return;
4040         }
4041         
4042         if (this.isVisible()) {
4043             Roo.log('hide');
4044             this.hide();
4045         } else {
4046             Roo.log('show');
4047             
4048             this.show(this.triggerEl, this.align, false);
4049         }
4050         
4051         if(this.stopEvent || e.getTarget().nodeName.toLowerCase() === 'i'){
4052             e.stopEvent();
4053         }
4054         
4055     },
4056        
4057     
4058     hideMenuItems : function()
4059     {
4060         Roo.log("hide Menu Items");
4061         if (!this.el) { 
4062             return;
4063         }
4064         
4065         this.el.select('.open',true).each(function(aa) {
4066             
4067             aa.removeClass('open');
4068          
4069         });
4070     },
4071     addxtypeChild : function (tree, cntr) {
4072         var comp= Roo.bootstrap.menu.Menu.superclass.addxtypeChild.call(this, tree, cntr);
4073           
4074         this.menuitems.add(comp);
4075         return comp;
4076
4077     },
4078     getEl : function()
4079     {
4080         Roo.log(this.el);
4081         return this.el;
4082     },
4083     
4084     clear : function()
4085     {
4086         this.getEl().dom.innerHTML = '';
4087         this.menuitems.clear();
4088     }
4089 });
4090
4091  
4092  /**
4093  * @class Roo.bootstrap.menu.Item
4094  * @extends Roo.bootstrap.Component
4095  * @children  Roo.bootstrap.Button Roo.bootstrap.ButtonUploader Roo.bootstrap.Row Roo.bootstrap.Column Roo.bootstrap.Container
4096  * @parent Roo.bootstrap.menu.Menu
4097  * @licence LGPL
4098  * Bootstrap MenuItem class
4099  * 
4100  * @cfg {String} html the menu label
4101  * @cfg {String} href the link
4102  * @cfg {Boolean} preventDefault do not trigger A href on clicks (default false).
4103  * @cfg {Boolean} isContainer is it a container - just returns a drop down item..
4104  * @cfg {Boolean} active  used on sidebars to highlight active itesm
4105  * @cfg {String} fa favicon to show on left of menu item.
4106  * @cfg {Roo.bootsrap.Menu} menu the child menu.
4107  * 
4108  * 
4109  * @constructor
4110  * Create a new MenuItem
4111  * @param {Object} config The config object
4112  */
4113
4114
4115 Roo.bootstrap.menu.Item = function(config){
4116     Roo.bootstrap.menu.Item.superclass.constructor.call(this, config);
4117     this.addEvents({
4118         // raw events
4119         /**
4120          * @event click
4121          * The raw click event for the entire grid.
4122          * @param {Roo.bootstrap.menu.Item} this
4123          * @param {Roo.EventObject} e
4124          */
4125         "click" : true
4126     });
4127 };
4128
4129 Roo.extend(Roo.bootstrap.menu.Item, Roo.bootstrap.Component,  {
4130     
4131     href : false,
4132     html : false,
4133     preventDefault: false,
4134     isContainer : false,
4135     active : false,
4136     fa: false,
4137     
4138     getAutoCreate : function(){
4139         
4140         if(this.isContainer){
4141             return {
4142                 tag: 'li',
4143                 cls: 'dropdown-menu-item '
4144             };
4145         }
4146         var ctag = {
4147             tag: 'span',
4148             html: 'Link'
4149         };
4150         
4151         var anc = {
4152             tag : 'a',
4153             cls : 'dropdown-item',
4154             href : '#',
4155             cn : [  ]
4156         };
4157         
4158         if (this.fa !== false) {
4159             anc.cn.push({
4160                 tag : 'i',
4161                 cls : 'fa fa-' + this.fa
4162             });
4163         }
4164         
4165         anc.cn.push(ctag);
4166         
4167         
4168         var cfg= {
4169             tag: 'li',
4170             cls: 'dropdown-menu-item',
4171             cn: [ anc ]
4172         };
4173         if (this.parent().type == 'treeview') {
4174             cfg.cls = 'treeview-menu';
4175         }
4176         if (this.active) {
4177             cfg.cls += ' active';
4178         }
4179         
4180         
4181         
4182         anc.href = this.href || cfg.cn[0].href ;
4183         ctag.html = this.html || cfg.cn[0].html ;
4184         return cfg;
4185     },
4186     
4187     initEvents: function()
4188     {
4189         if (this.parent().type == 'treeview') {
4190             this.el.select('a').on('click', this.onClick, this);
4191         }
4192         
4193         if (this.menu) {
4194             this.menu.parentType = this.xtype;
4195             this.menu.triggerEl = this.el;
4196             this.menu = this.addxtype(Roo.apply({}, this.menu));
4197         }
4198         
4199     },
4200     onClick : function(e)
4201     {
4202         //Roo.log('item on click ');
4203         
4204         if(this.href === false || this.preventDefault){
4205             e.preventDefault();
4206         }
4207         //this.parent().hideMenuItems();
4208         
4209         this.fireEvent('click', this, e);
4210     },
4211     getEl : function()
4212     {
4213         return this.el;
4214     } 
4215 });
4216
4217  
4218
4219  
4220
4221   
4222 /**
4223  * @class Roo.bootstrap.menu.Separator
4224  * @extends Roo.bootstrap.Component
4225  * @licence LGPL
4226  * @parent Roo.bootstrap.menu.Menu
4227  * Bootstrap Separator class
4228  * 
4229  * @constructor
4230  * Create a new Separator
4231  * @param {Object} config The config object
4232  */
4233
4234
4235 Roo.bootstrap.menu.Separator = function(config){
4236     Roo.bootstrap.menu.Separator.superclass.constructor.call(this, config);
4237 };
4238
4239 Roo.extend(Roo.bootstrap.menu.Separator, Roo.bootstrap.Component,  {
4240     
4241     getAutoCreate : function(){
4242         var cfg = {
4243             tag : 'li',
4244             cls: 'dropdown-divider divider'
4245         };
4246         
4247         return cfg;
4248     }
4249    
4250 });
4251
4252  
4253
4254  
4255 /*
4256 * Licence: LGPL
4257 */
4258
4259 /**
4260  * @class Roo.bootstrap.Modal
4261  * @extends Roo.bootstrap.Component
4262  * @parent none builder
4263  * @children Roo.bootstrap.Component
4264  * Bootstrap Modal class
4265  * @cfg {String} title Title of dialog
4266  * @cfg {String} html - the body of the dialog (for simple ones) - you can also use template..
4267  * @cfg {Roo.Template} tmpl - a template with variables. to use it, add a handler in show:method  adn
4268  * @cfg {Boolean} specificTitle default false
4269  * @cfg {Roo.bootstrap.Button} buttons[] Array of buttons or standard button set..
4270  * @cfg {String} buttonPosition (left|right|center) default right (DEPRICATED) - use mr-auto on buttons to put them on the left
4271  * @cfg {Boolean} animate default true
4272  * @cfg {Boolean} allow_close default true
4273  * @cfg {Boolean} fitwindow default false
4274  * @cfg {Boolean} bodyOverflow should the body element have overflow auto added default false
4275  * @cfg {Number} width fixed width - usefull for chrome extension only really.
4276  * @cfg {Number} height fixed height - usefull for chrome extension only really.
4277  * @cfg {String} size (sm|lg|xl) default empty
4278  * @cfg {Number} max_width set the max width of modal
4279  * @cfg {Boolean} editableTitle can the title be edited
4280
4281  *
4282  *
4283  * @constructor
4284  * Create a new Modal Dialog
4285  * @param {Object} config The config object
4286  */
4287
4288 Roo.bootstrap.Modal = function(config){
4289     Roo.bootstrap.Modal.superclass.constructor.call(this, config);
4290     this.addEvents({
4291         // raw events
4292         /**
4293          * @event btnclick
4294          * The raw btnclick event for the button
4295          * @param {Roo.EventObject} e
4296          */
4297         "btnclick" : true,
4298         /**
4299          * @event resize
4300          * Fire when dialog resize
4301          * @param {Roo.bootstrap.Modal} this
4302          * @param {Roo.EventObject} e
4303          */
4304         "resize" : true,
4305         /**
4306          * @event titlechanged
4307          * Fire when the editable title has been changed
4308          * @param {Roo.bootstrap.Modal} this
4309          * @param {Roo.EventObject} value
4310          */
4311         "titlechanged" : true 
4312         
4313     });
4314     this.buttons = this.buttons || [];
4315
4316     if (this.tmpl) {
4317         this.tmpl = Roo.factory(this.tmpl);
4318     }
4319
4320 };
4321
4322 Roo.extend(Roo.bootstrap.Modal, Roo.bootstrap.Component,  {
4323
4324     title : 'test dialog',
4325
4326     buttons : false,
4327
4328     // set on load...
4329
4330     html: false,
4331
4332     tmp: false,
4333
4334     specificTitle: false,
4335
4336     buttonPosition: 'right',
4337
4338     allow_close : true,
4339
4340     animate : true,
4341
4342     fitwindow: false,
4343     
4344      // private
4345     dialogEl: false,
4346     bodyEl:  false,
4347     footerEl:  false,
4348     titleEl:  false,
4349     closeEl:  false,
4350
4351     size: '',
4352     
4353     max_width: 0,
4354     
4355     max_height: 0,
4356     
4357     fit_content: false,
4358     editableTitle  : false,
4359
4360     onRender : function(ct, position)
4361     {
4362         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
4363
4364         if(!this.el){
4365             var cfg = Roo.apply({},  this.getAutoCreate());
4366             cfg.id = Roo.id();
4367             //if(!cfg.name){
4368             //    cfg.name = typeof(this.name) == 'undefined' ? this.id : this.name;
4369             //}
4370             //if (!cfg.name.length) {
4371             //    delete cfg.name;
4372            // }
4373             if (this.cls) {
4374                 cfg.cls += ' ' + this.cls;
4375             }
4376             if (this.style) {
4377                 cfg.style = this.style;
4378             }
4379             this.el = Roo.get(document.body).createChild(cfg, position);
4380         }
4381         //var type = this.el.dom.type;
4382
4383
4384         if(this.tabIndex !== undefined){
4385             this.el.dom.setAttribute('tabIndex', this.tabIndex);
4386         }
4387
4388         this.dialogEl = this.el.select('.modal-dialog',true).first();
4389         this.bodyEl = this.el.select('.modal-body',true).first();
4390         this.closeEl = this.el.select('.modal-header .close', true).first();
4391         this.headerEl = this.el.select('.modal-header',true).first();
4392         this.titleEl = this.el.select('.modal-title',true).first();
4393         this.footerEl = this.el.select('.modal-footer',true).first();
4394
4395         this.maskEl = Roo.DomHelper.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
4396         
4397         //this.el.addClass("x-dlg-modal");
4398
4399         if (this.buttons.length) {
4400             Roo.each(this.buttons, function(bb) {
4401                 var b = Roo.apply({}, bb);
4402                 b.xns = b.xns || Roo.bootstrap;
4403                 b.xtype = b.xtype || 'Button';
4404                 if (typeof(b.listeners) == 'undefined') {
4405                     b.listeners = { click : this.onButtonClick.createDelegate(this)  };
4406                 }
4407
4408                 var btn = Roo.factory(b);
4409
4410                 btn.render(this.getButtonContainer());
4411
4412             },this);
4413         }
4414         // render the children.
4415         var nitems = [];
4416
4417         if(typeof(this.items) != 'undefined'){
4418             var items = this.items;
4419             delete this.items;
4420
4421             for(var i =0;i < items.length;i++) {
4422                 // we force children not to montor widnow resize  - as we do that for them.
4423                 items[i].monitorWindowResize = false;
4424                 nitems.push(this.addxtype(Roo.apply({}, items[i])));
4425             }
4426         }
4427
4428         this.items = nitems;
4429
4430         // where are these used - they used to be body/close/footer
4431
4432
4433         this.initEvents();
4434         //this.el.addClass([this.fieldClass, this.cls]);
4435
4436     },
4437
4438     getAutoCreate : function()
4439     {
4440         // we will default to modal-body-overflow - might need to remove or make optional later.
4441         var bdy = {
4442                 cls : 'modal-body ' + (this.bodyOverflow ? 'overflow-auto' : ''), 
4443                 html : this.html || ''
4444         };
4445
4446         var title = {
4447             tag: 'h5',
4448             cls : 'modal-title',
4449             html : this.title
4450         };
4451
4452         if(this.specificTitle){ // WTF is this?
4453             title = this.title;
4454         }
4455
4456         var header = [];
4457         if (this.allow_close && Roo.bootstrap.version == 3) {
4458             header.push({
4459                 tag: 'button',
4460                 cls : 'close',
4461                 html : '&times'
4462             });
4463         }
4464
4465         header.push(title);
4466
4467         if (this.editableTitle) {
4468             header.push({
4469                 cls: 'form-control roo-editable-title d-none',
4470                 tag: 'input',
4471                 type: 'text'
4472             });
4473         }
4474         
4475         if (this.allow_close && Roo.bootstrap.version == 4) {
4476             header.push({
4477                 tag: 'button',
4478                 cls : 'close',
4479                 html : '&times'
4480             });
4481         }
4482         
4483         var size = '';
4484
4485         if(this.size.length){
4486             size = 'modal-' + this.size;
4487         }
4488         
4489         var footer = Roo.bootstrap.version == 3 ?
4490             {
4491                 cls : 'modal-footer',
4492                 cn : [
4493                     {
4494                         tag: 'div',
4495                         cls: 'btn-' + this.buttonPosition
4496                     }
4497                 ]
4498
4499             } :
4500             {  // BS4 uses mr-auto on left buttons....
4501                 cls : 'modal-footer'
4502             };
4503
4504             
4505
4506         
4507         
4508         var modal = {
4509             cls: "modal",
4510              cn : [
4511                 {
4512                     cls: "modal-dialog " + size,
4513                     cn : [
4514                         {
4515                             cls : "modal-content",
4516                             cn : [
4517                                 {
4518                                     cls : 'modal-header',
4519                                     cn : header
4520                                 },
4521                                 bdy,
4522                                 footer
4523                             ]
4524
4525                         }
4526                     ]
4527
4528                 }
4529             ]
4530         };
4531
4532         if(this.animate){
4533             modal.cls += ' fade';
4534         }
4535
4536         return modal;
4537
4538     },
4539     getChildContainer : function() {
4540
4541          return this.bodyEl;
4542
4543     },
4544     getButtonContainer : function() {
4545         
4546          return Roo.bootstrap.version == 4 ?
4547             this.el.select('.modal-footer',true).first()
4548             : this.el.select('.modal-footer div',true).first();
4549
4550     },
4551     
4552     closeClick : function()
4553     {
4554         this.hide();
4555     },
4556     
4557     initEvents : function()
4558     {
4559         if (this.allow_close) {
4560             this.closeEl.on('click', this.closeClick, this);
4561         }
4562         Roo.EventManager.onWindowResize(this.resize, this, true);
4563         if (this.editableTitle) {
4564             this.headerEditEl =  this.headerEl.select('.form-control',true).first();
4565             this.headerEl.on('click', function() { this.toggleHeaderInput(true) } , this);
4566             this.headerEditEl.on('keyup', function(e) {
4567                     if([  e.RETURN , e.TAB , e.ESC ].indexOf(e.keyCode) > -1) {
4568                         this.toggleHeaderInput(false)
4569                     }
4570                 }, this);
4571             this.headerEditEl.on('blur', function(e) {
4572                 this.toggleHeaderInput(false)
4573             },this);
4574         }
4575
4576     },
4577   
4578
4579     resize : function()
4580     {
4581         this.maskEl.setSize(
4582             Roo.lib.Dom.getViewWidth(true),
4583             Roo.lib.Dom.getViewHeight(true)
4584         );
4585         
4586         if (this.fitwindow) {
4587             
4588            this.dialogEl.setStyle( { 'max-width' : '100%' });
4589             this.setSize(
4590                 this.width || Roo.lib.Dom.getViewportWidth(true) - 30,
4591                 this.height || Roo.lib.Dom.getViewportHeight(true) // catering margin-top 30 margin-bottom 30
4592             );
4593             return;
4594         }
4595         
4596         if(this.max_width !== 0) {
4597             
4598             var w = Math.min(this.max_width, Roo.lib.Dom.getViewportWidth(true) - 30);
4599             
4600             if(this.height) {
4601                 this.setSize(w, this.height);
4602                 return;
4603             }
4604             
4605             if(this.max_height) {
4606                 this.setSize(w,Math.min(
4607                     this.max_height,
4608                     Roo.lib.Dom.getViewportHeight(true) - 60
4609                 ));
4610                 
4611                 return;
4612             }
4613             
4614             if(!this.fit_content) {
4615                 this.setSize(w, Roo.lib.Dom.getViewportHeight(true) - 60);
4616                 return;
4617             }
4618             
4619             this.setSize(w, Math.min(
4620                 60 +
4621                 this.headerEl.getHeight() + 
4622                 this.footerEl.getHeight() + 
4623                 this.getChildHeight(this.bodyEl.dom.childNodes),
4624                 Roo.lib.Dom.getViewportHeight(true) - 60)
4625             );
4626         }
4627         
4628     },
4629
4630     setSize : function(w,h)
4631     {
4632         if (!w && !h) {
4633             return;
4634         }
4635         
4636         this.resizeTo(w,h);
4637         // any layout/border etc.. resize..
4638         (function () {
4639             this.items.forEach( function(e) {
4640                 e.layout ? e.layout() : false;
4641
4642             });
4643         }).defer(100,this);
4644         
4645     },
4646
4647     show : function() {
4648
4649         if (!this.rendered) {
4650             this.render();
4651         }
4652         this.toggleHeaderInput(false);
4653         //this.el.setStyle('display', 'block');
4654         this.el.removeClass('hideing');
4655         this.el.dom.style.display='block';
4656         
4657         Roo.get(document.body).addClass('modal-open');
4658  
4659         if(this.animate){  // element has 'fade'  - so stuff happens after .3s ?- not sure why the delay?
4660             
4661             (function(){
4662                 this.el.addClass('show');
4663                 this.el.addClass('in');
4664             }).defer(50, this);
4665         }else{
4666             this.el.addClass('show');
4667             this.el.addClass('in');
4668         }
4669
4670         // not sure how we can show data in here..
4671         //if (this.tmpl) {
4672         //    this.getChildContainer().dom.innerHTML = this.tmpl.applyTemplate(this);
4673         //}
4674
4675         Roo.get(document.body).addClass("x-body-masked");
4676         
4677         this.maskEl.setSize(Roo.lib.Dom.getViewWidth(true),   Roo.lib.Dom.getViewHeight(true));
4678         this.maskEl.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
4679         this.maskEl.dom.style.display = 'block';
4680         this.maskEl.addClass('show');
4681         
4682         
4683         this.resize();
4684         
4685         this.fireEvent('show', this);
4686
4687         // set zindex here - otherwise it appears to be ignored...
4688         this.el.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
4689         
4690         
4691         // this is for children that are... layout.Border 
4692         (function () {
4693             this.items.forEach( function(e) {
4694                 e.layout ? e.layout() : false;
4695
4696             });
4697         }).defer(100,this);
4698
4699     },
4700     hide : function()
4701     {
4702         if(this.fireEvent("beforehide", this) !== false){
4703             
4704             this.maskEl.removeClass('show');
4705             
4706             this.maskEl.dom.style.display = '';
4707             Roo.get(document.body).removeClass("x-body-masked");
4708             this.el.removeClass('in');
4709             this.el.select('.modal-dialog', true).first().setStyle('transform','');
4710
4711             if(this.animate){ // why
4712                 this.el.addClass('hideing');
4713                 this.el.removeClass('show');
4714                 (function(){
4715                     if (!this.el.hasClass('hideing')) {
4716                         return; // it's been shown again...
4717                     }
4718                     
4719                     this.el.dom.style.display='';
4720
4721                     Roo.get(document.body).removeClass('modal-open');
4722                     this.el.removeClass('hideing');
4723                 }).defer(150,this);
4724                 
4725             }else{
4726                 this.el.removeClass('show');
4727                 this.el.dom.style.display='';
4728                 Roo.get(document.body).removeClass('modal-open');
4729
4730             }
4731             this.fireEvent('hide', this);
4732         }
4733     },
4734     isVisible : function()
4735     {
4736         
4737         return this.el.hasClass('show') && !this.el.hasClass('hideing');
4738         
4739     },
4740
4741     addButton : function(str, cb)
4742     {
4743
4744
4745         var b = Roo.apply({}, { html : str } );
4746         b.xns = b.xns || Roo.bootstrap;
4747         b.xtype = b.xtype || 'Button';
4748         if (typeof(b.listeners) == 'undefined') {
4749             b.listeners = { click : cb.createDelegate(this)  };
4750         }
4751
4752         var btn = Roo.factory(b);
4753
4754         btn.render(this.getButtonContainer());
4755
4756         return btn;
4757
4758     },
4759
4760     setDefaultButton : function(btn)
4761     {
4762         //this.el.select('.modal-footer').()
4763     },
4764
4765     resizeTo: function(w,h)
4766     {
4767         this.dialogEl.setWidth(w);
4768         
4769         var diff = this.headerEl.getHeight() + this.footerEl.getHeight() + 60; // dialog margin-bottom: 30  
4770
4771         this.bodyEl.setHeight(h - diff);
4772         
4773         this.fireEvent('resize', this);
4774     },
4775     
4776     setContentSize  : function(w, h)
4777     {
4778
4779     },
4780     onButtonClick: function(btn,e)
4781     {
4782         //Roo.log([a,b,c]);
4783         this.fireEvent('btnclick', btn.name, e);
4784     },
4785      /**
4786      * Set the title of the Dialog
4787      * @param {String} str new Title
4788      */
4789     setTitle: function(str) {
4790         this.titleEl.dom.innerHTML = str;
4791         this.title = str;
4792     },
4793     /**
4794      * Set the body of the Dialog
4795      * @param {String} str new Title
4796      */
4797     setBody: function(str) {
4798         this.bodyEl.dom.innerHTML = str;
4799     },
4800     /**
4801      * Set the body of the Dialog using the template
4802      * @param {Obj} data - apply this data to the template and replace the body contents.
4803      */
4804     applyBody: function(obj)
4805     {
4806         if (!this.tmpl) {
4807             Roo.log("Error - using apply Body without a template");
4808             //code
4809         }
4810         this.tmpl.overwrite(this.bodyEl, obj);
4811     },
4812     
4813     getChildHeight : function(child_nodes)
4814     {
4815         if(
4816             !child_nodes ||
4817             child_nodes.length == 0
4818         ) {
4819             return 0;
4820         }
4821         
4822         var child_height = 0;
4823         
4824         for(var i = 0; i < child_nodes.length; i++) {
4825             
4826             /*
4827             * for modal with tabs...
4828             if(child_nodes[i].classList.contains('roo-layout-panel')) {
4829                 
4830                 var layout_childs = child_nodes[i].childNodes;
4831                 
4832                 for(var j = 0; j < layout_childs.length; j++) {
4833                     
4834                     if(layout_childs[j].classList.contains('roo-layout-panel-body')) {
4835                         
4836                         var layout_body_childs = layout_childs[j].childNodes;
4837                         
4838                         for(var k = 0; k < layout_body_childs.length; k++) {
4839                             
4840                             if(layout_body_childs[k].classList.contains('navbar')) {
4841                                 child_height += layout_body_childs[k].offsetHeight;
4842                                 continue;
4843                             }
4844                             
4845                             if(layout_body_childs[k].classList.contains('roo-layout-tabs-body')) {
4846                                 
4847                                 var layout_body_tab_childs = layout_body_childs[k].childNodes;
4848                                 
4849                                 for(var m = 0; m < layout_body_tab_childs.length; m++) {
4850                                     
4851                                     if(layout_body_tab_childs[m].classList.contains('roo-layout-active-content')) {
4852                                         child_height += this.getChildHeight(layout_body_tab_childs[m].childNodes);
4853                                         continue;
4854                                     }
4855                                     
4856                                 }
4857                                 
4858                             }
4859                             
4860                         }
4861                     }
4862                 }
4863                 continue;
4864             }
4865             */
4866             
4867             child_height += child_nodes[i].offsetHeight;
4868             // Roo.log(child_nodes[i].offsetHeight);
4869         }
4870         
4871         return child_height;
4872     },
4873     toggleHeaderInput : function(is_edit)
4874     {
4875         if (!this.editableTitle) {
4876             return; // not editable.
4877         }
4878         if (is_edit && this.is_header_editing) {
4879             return; // already editing..
4880         }
4881         if (is_edit) {
4882     
4883             this.headerEditEl.dom.value = this.title;
4884             this.headerEditEl.removeClass('d-none');
4885             this.headerEditEl.dom.focus();
4886             this.titleEl.addClass('d-none');
4887             
4888             this.is_header_editing = true;
4889             return
4890         }
4891         // flip back to not editing.
4892         this.title = this.headerEditEl.dom.value;
4893         this.headerEditEl.addClass('d-none');
4894         this.titleEl.removeClass('d-none');
4895         this.titleEl.dom.innerHTML = String.format('{0}', this.title);
4896         this.is_header_editing = false;
4897         this.fireEvent('titlechanged', this, this.title);
4898     
4899             
4900         
4901     }
4902
4903 });
4904
4905
4906 Roo.apply(Roo.bootstrap.Modal,  {
4907     /**
4908          * Button config that displays a single OK button
4909          * @type Object
4910          */
4911         OK :  [{
4912             name : 'ok',
4913             weight : 'primary',
4914             html : 'OK'
4915         }],
4916         /**
4917          * Button config that displays Yes and No buttons
4918          * @type Object
4919          */
4920         YESNO : [
4921             {
4922                 name  : 'no',
4923                 html : 'No'
4924             },
4925             {
4926                 name  :'yes',
4927                 weight : 'primary',
4928                 html : 'Yes'
4929             }
4930         ],
4931
4932         /**
4933          * Button config that displays OK and Cancel buttons
4934          * @type Object
4935          */
4936         OKCANCEL : [
4937             {
4938                name : 'cancel',
4939                 html : 'Cancel'
4940             },
4941             {
4942                 name : 'ok',
4943                 weight : 'primary',
4944                 html : 'OK'
4945             }
4946         ],
4947         /**
4948          * Button config that displays Yes, No and Cancel buttons
4949          * @type Object
4950          */
4951         YESNOCANCEL : [
4952             {
4953                 name : 'yes',
4954                 weight : 'primary',
4955                 html : 'Yes'
4956             },
4957             {
4958                 name : 'no',
4959                 html : 'No'
4960             },
4961             {
4962                 name : 'cancel',
4963                 html : 'Cancel'
4964             }
4965         ],
4966         
4967         zIndex : 10001
4968 });
4969
4970 /*
4971  * - LGPL
4972  *
4973  * messagebox - can be used as a replace
4974  * 
4975  */
4976 /**
4977  * @class Roo.MessageBox
4978  * Utility class for generating different styles of message boxes.  The alias Roo.Msg can also be used.
4979  * Example usage:
4980  *<pre><code>
4981 // Basic alert:
4982 Roo.Msg.alert('Status', 'Changes saved successfully.');
4983
4984 // Prompt for user data:
4985 Roo.Msg.prompt('Name', 'Please enter your name:', function(btn, text){
4986     if (btn == 'ok'){
4987         // process text value...
4988     }
4989 });
4990
4991 // Show a dialog using config options:
4992 Roo.Msg.show({
4993    title:'Save Changes?',
4994    msg: 'Your are closing a tab that has unsaved changes. Would you like to save your changes?',
4995    buttons: Roo.Msg.YESNOCANCEL,
4996    fn: processResult,
4997    animEl: 'elId'
4998 });
4999 </code></pre>
5000  * @static
5001  */
5002 Roo.bootstrap.MessageBox = function(){
5003     var dlg, opt, mask, waitTimer;
5004     var bodyEl, msgEl, textboxEl, textareaEl, progressEl, pp;
5005     var buttons, activeTextEl, bwidth;
5006
5007     
5008     // private
5009     var handleButton = function(button){
5010         dlg.hide();
5011         Roo.callback(opt.fn, opt.scope||window, [button, activeTextEl.dom.value], 1);
5012     };
5013
5014     // private
5015     var handleHide = function(){
5016         if(opt && opt.cls){
5017             dlg.el.removeClass(opt.cls);
5018         }
5019         //if(waitTimer){
5020         //    Roo.TaskMgr.stop(waitTimer);
5021         //    waitTimer = null;
5022         //}
5023     };
5024
5025     // private
5026     var updateButtons = function(b){
5027         var width = 0;
5028         if(!b){
5029             buttons["ok"].hide();
5030             buttons["cancel"].hide();
5031             buttons["yes"].hide();
5032             buttons["no"].hide();
5033             dlg.footerEl.hide();
5034             
5035             return width;
5036         }
5037         dlg.footerEl.show();
5038         for(var k in buttons){
5039             if(typeof buttons[k] != "function"){
5040                 if(b[k]){
5041                     buttons[k].show();
5042                     buttons[k].setText(typeof b[k] == "string" ? b[k] : Roo.bootstrap.MessageBox.buttonText[k]);
5043                     width += buttons[k].el.getWidth()+15;
5044                 }else{
5045                     buttons[k].hide();
5046                 }
5047             }
5048         }
5049         return width;
5050     };
5051
5052     // private
5053     var handleEsc = function(d, k, e){
5054         if(opt && opt.closable !== false){
5055             dlg.hide();
5056         }
5057         if(e){
5058             e.stopEvent();
5059         }
5060     };
5061
5062     return {
5063         /**
5064          * Returns a reference to the underlying {@link Roo.BasicDialog} element
5065          * @return {Roo.BasicDialog} The BasicDialog element
5066          */
5067         getDialog : function(){
5068            if(!dlg){
5069                 dlg = new Roo.bootstrap.Modal( {
5070                     //draggable: true,
5071                     //resizable:false,
5072                     //constraintoviewport:false,
5073                     //fixedcenter:true,
5074                     //collapsible : false,
5075                     //shim:true,
5076                     //modal: true,
5077                 //    width: 'auto',
5078                   //  height:100,
5079                     //buttonAlign:"center",
5080                     closeClick : function(){
5081                         if(opt && opt.buttons && opt.buttons.no && !opt.buttons.cancel){
5082                             handleButton("no");
5083                         }else{
5084                             handleButton("cancel");
5085                         }
5086                     }
5087                 });
5088                 dlg.render();
5089                 dlg.on("hide", handleHide);
5090                 mask = dlg.mask;
5091                 //dlg.addKeyListener(27, handleEsc);
5092                 buttons = {};
5093                 this.buttons = buttons;
5094                 var bt = this.buttonText;
5095                 buttons["ok"] = dlg.addButton(bt["ok"], handleButton.createCallback("ok"));
5096                 buttons["yes"] = dlg.addButton(bt["yes"], handleButton.createCallback("yes"));
5097                 buttons["no"] = dlg.addButton(bt["no"], handleButton.createCallback("no"));
5098                 buttons["cancel"] = dlg.addButton(bt["cancel"], handleButton.createCallback("cancel"));
5099                 //Roo.log(buttons);
5100                 bodyEl = dlg.bodyEl.createChild({
5101
5102                     html:'<span class="roo-mb-text"></span><br /><input type="text" class="roo-mb-input" />' +
5103                         '<textarea class="roo-mb-textarea"></textarea>' +
5104                         '<div class="roo-mb-progress-wrap"><div class="roo-mb-progress"><div class="roo-mb-progress-bar">&#160;</div></div></div>'
5105                 });
5106                 msgEl = bodyEl.dom.firstChild;
5107                 textboxEl = Roo.get(bodyEl.dom.childNodes[2]);
5108                 textboxEl.enableDisplayMode();
5109                 textboxEl.addKeyListener([10,13], function(){
5110                     if(dlg.isVisible() && opt && opt.buttons){
5111                         if(opt.buttons.ok){
5112                             handleButton("ok");
5113                         }else if(opt.buttons.yes){
5114                             handleButton("yes");
5115                         }
5116                     }
5117                 });
5118                 textareaEl = Roo.get(bodyEl.dom.childNodes[3]);
5119                 textareaEl.enableDisplayMode();
5120                 progressEl = Roo.get(bodyEl.dom.childNodes[4]);
5121                 progressEl.enableDisplayMode();
5122                 
5123                 // This is supposed to be the progessElement.. but I think it's controlling the height of everything..
5124                 var pf = progressEl.dom.firstChild;
5125                 if (pf) {
5126                     pp = Roo.get(pf.firstChild);
5127                     pp.setHeight(pf.offsetHeight);
5128                 }
5129                 
5130             }
5131             return dlg;
5132         },
5133
5134         /**
5135          * Updates the message box body text
5136          * @param {String} text (optional) Replaces the message box element's innerHTML with the specified string (defaults to
5137          * the XHTML-compliant non-breaking space character '&amp;#160;')
5138          * @return {Roo.MessageBox} This message box
5139          */
5140         updateText : function(text)
5141         {
5142             if(!dlg.isVisible() && !opt.width){
5143                 dlg.dialogEl.setStyle({ 'max-width' : this.maxWidth});
5144                 // dlg.resizeTo(this.maxWidth, 100); // forcing the height breaks long alerts()
5145             }
5146             msgEl.innerHTML = text || '&#160;';
5147       
5148             var cw =  Math.max(msgEl.offsetWidth, msgEl.parentNode.scrollWidth);
5149             //Roo.log("guesed size: " + JSON.stringify([cw,msgEl.offsetWidth, msgEl.parentNode.scrollWidth]));
5150             var w = Math.max(
5151                     Math.min(opt.width || cw , this.maxWidth), 
5152                     Math.max(opt.minWidth || this.minWidth, bwidth)
5153             );
5154             if(opt.prompt){
5155                 activeTextEl.setWidth(w);
5156             }
5157             if(dlg.isVisible()){
5158                 dlg.fixedcenter = false;
5159             }
5160             // to big, make it scroll. = But as usual stupid IE does not support
5161             // !important..
5162             
5163             if ( bodyEl.getHeight() > (Roo.lib.Dom.getViewHeight() - 100)) {
5164                 bodyEl.setHeight ( Roo.lib.Dom.getViewHeight() - 100 );
5165                 bodyEl.dom.style.overflowY = 'auto' + ( Roo.isIE ? '' : ' !important');
5166             } else {
5167                 bodyEl.dom.style.height = '';
5168                 bodyEl.dom.style.overflowY = '';
5169             }
5170             if (cw > w) {
5171                 bodyEl.dom.style.get = 'auto' + ( Roo.isIE ? '' : ' !important');
5172             } else {
5173                 bodyEl.dom.style.overflowX = '';
5174             }
5175             
5176             dlg.setContentSize(w, bodyEl.getHeight());
5177             if(dlg.isVisible()){
5178                 dlg.fixedcenter = true;
5179             }
5180             return this;
5181         },
5182
5183         /**
5184          * Updates a progress-style message box's text and progress bar.  Only relevant on message boxes
5185          * initiated via {@link Roo.MessageBox#progress} or by calling {@link Roo.MessageBox#show} with progress: true.
5186          * @param {Number} value Any number between 0 and 1 (e.g., .5)
5187          * @param {String} text (optional) If defined, the message box's body text is replaced with the specified string (defaults to undefined)
5188          * @return {Roo.MessageBox} This message box
5189          */
5190         updateProgress : function(value, text){
5191             if(text){
5192                 this.updateText(text);
5193             }
5194             
5195             if (pp) { // weird bug on my firefox - for some reason this is not defined
5196                 pp.setWidth(Math.floor(value*progressEl.dom.firstChild.offsetWidth));
5197                 pp.setHeight(Math.floor(progressEl.dom.firstChild.offsetHeight));
5198             }
5199             return this;
5200         },        
5201
5202         /**
5203          * Returns true if the message box is currently displayed
5204          * @return {Boolean} True if the message box is visible, else false
5205          */
5206         isVisible : function(){
5207             return dlg && dlg.isVisible();  
5208         },
5209
5210         /**
5211          * Hides the message box if it is displayed
5212          */
5213         hide : function(){
5214             if(this.isVisible()){
5215                 dlg.hide();
5216             }  
5217         },
5218
5219         /**
5220          * Displays a new message box, or reinitializes an existing message box, based on the config options
5221          * passed in. All functions (e.g. prompt, alert, etc) on MessageBox call this function internally.
5222          * The following config object properties are supported:
5223          * <pre>
5224 Property    Type             Description
5225 ----------  ---------------  ------------------------------------------------------------------------------------
5226 animEl            String/Element   An id or Element from which the message box should animate as it opens and
5227                                    closes (defaults to undefined)
5228 buttons           Object/Boolean   A button config object (e.g., Roo.MessageBox.OKCANCEL or {ok:'Foo',
5229                                    cancel:'Bar'}), or false to not show any buttons (defaults to false)
5230 closable          Boolean          False to hide the top-right close button (defaults to true).  Note that
5231                                    progress and wait dialogs will ignore this property and always hide the
5232                                    close button as they can only be closed programmatically.
5233 cls               String           A custom CSS class to apply to the message box element
5234 defaultTextHeight Number           The default height in pixels of the message box's multiline textarea if
5235                                    displayed (defaults to 75)
5236 fn                Function         A callback function to execute after closing the dialog.  The arguments to the
5237                                    function will be btn (the name of the button that was clicked, if applicable,
5238                                    e.g. "ok"), and text (the value of the active text field, if applicable).
5239                                    Progress and wait dialogs will ignore this option since they do not respond to
5240                                    user actions and can only be closed programmatically, so any required function
5241                                    should be called by the same code after it closes the dialog.
5242 icon              String           A CSS class that provides a background image to be used as an icon for
5243                                    the dialog (e.g., Roo.MessageBox.WARNING or 'custom-class', defaults to '')
5244 maxWidth          Number           The maximum width in pixels of the message box (defaults to 600)
5245 minWidth          Number           The minimum width in pixels of the message box (defaults to 100)
5246 modal             Boolean          False to allow user interaction with the page while the message box is
5247                                    displayed (defaults to true)
5248 msg               String           A string that will replace the existing message box body text (defaults
5249                                    to the XHTML-compliant non-breaking space character '&#160;')
5250 multiline         Boolean          True to prompt the user to enter multi-line text (defaults to false)
5251 progress          Boolean          True to display a progress bar (defaults to false)
5252 progressText      String           The text to display inside the progress bar if progress = true (defaults to '')
5253 prompt            Boolean          True to prompt the user to enter single-line text (defaults to false)
5254 proxyDrag         Boolean          True to display a lightweight proxy while dragging (defaults to false)
5255 title             String           The title text
5256 value             String           The string value to set into the active textbox element if displayed
5257 wait              Boolean          True to display a progress bar (defaults to false)
5258 width             Number           The width of the dialog in pixels
5259 </pre>
5260          *
5261          * Example usage:
5262          * <pre><code>
5263 Roo.Msg.show({
5264    title: 'Address',
5265    msg: 'Please enter your address:',
5266    width: 300,
5267    buttons: Roo.MessageBox.OKCANCEL,
5268    multiline: true,
5269    fn: saveAddress,
5270    animEl: 'addAddressBtn'
5271 });
5272 </code></pre>
5273          * @param {Object} config Configuration options
5274          * @return {Roo.MessageBox} This message box
5275          */
5276         show : function(options)
5277         {
5278             
5279             // this causes nightmares if you show one dialog after another
5280             // especially on callbacks..
5281              
5282             if(this.isVisible()){
5283                 
5284                 this.hide();
5285                 Roo.log("[Roo.Messagebox] Show called while message displayed:" );
5286                 Roo.log("Old Dialog Message:" +  msgEl.innerHTML );
5287                 Roo.log("New Dialog Message:" +  options.msg )
5288                 //this.alert("ERROR", "Multiple dialogs where displayed at the same time");
5289                 //throw "Roo.MessageBox ERROR : Multiple dialogs where displayed at the same time";
5290                 
5291             }
5292             var d = this.getDialog();
5293             opt = options;
5294             d.setTitle(opt.title || "&#160;");
5295             d.closeEl.setDisplayed(opt.closable !== false);
5296             activeTextEl = textboxEl;
5297             opt.prompt = opt.prompt || (opt.multiline ? true : false);
5298             if(opt.prompt){
5299                 if(opt.multiline){
5300                     textboxEl.hide();
5301                     textareaEl.show();
5302                     textareaEl.setHeight(typeof opt.multiline == "number" ?
5303                         opt.multiline : this.defaultTextHeight);
5304                     activeTextEl = textareaEl;
5305                 }else{
5306                     textboxEl.show();
5307                     textareaEl.hide();
5308                 }
5309             }else{
5310                 textboxEl.hide();
5311                 textareaEl.hide();
5312             }
5313             progressEl.setDisplayed(opt.progress === true);
5314             if (opt.progress) {
5315                 d.animate = false; // do not animate progress, as it may not have finished animating before we close it..
5316             }
5317             this.updateProgress(0);
5318             activeTextEl.dom.value = opt.value || "";
5319             if(opt.prompt){
5320                 dlg.setDefaultButton(activeTextEl);
5321             }else{
5322                 var bs = opt.buttons;
5323                 var db = null;
5324                 if(bs && bs.ok){
5325                     db = buttons["ok"];
5326                 }else if(bs && bs.yes){
5327                     db = buttons["yes"];
5328                 }
5329                 dlg.setDefaultButton(db);
5330             }
5331             bwidth = updateButtons(opt.buttons);
5332             this.updateText(opt.msg);
5333             if(opt.cls){
5334                 d.el.addClass(opt.cls);
5335             }
5336             d.proxyDrag = opt.proxyDrag === true;
5337             d.modal = opt.modal !== false;
5338             d.mask = opt.modal !== false ? mask : false;
5339             if(!d.isVisible()){
5340                 // force it to the end of the z-index stack so it gets a cursor in FF
5341                 document.body.appendChild(dlg.el.dom);
5342                 d.animateTarget = null;
5343                 d.show(options.animEl);
5344             }
5345             return this;
5346         },
5347
5348         /**
5349          * Displays a message box with a progress bar.  This message box has no buttons and is not closeable by
5350          * the user.  You are responsible for updating the progress bar as needed via {@link Roo.MessageBox#updateProgress}
5351          * and closing the message box when the process is complete.
5352          * @param {String} title The title bar text
5353          * @param {String} msg The message box body text
5354          * @return {Roo.MessageBox} This message box
5355          */
5356         progress : function(title, msg){
5357             this.show({
5358                 title : title,
5359                 msg : msg,
5360                 buttons: false,
5361                 progress:true,
5362                 closable:false,
5363                 minWidth: this.minProgressWidth,
5364                 modal : true
5365             });
5366             return this;
5367         },
5368
5369         /**
5370          * Displays a standard read-only message box with an OK button (comparable to the basic JavaScript Window.alert).
5371          * If a callback function is passed it will be called after the user clicks the button, and the
5372          * id of the button that was clicked will be passed as the only parameter to the callback
5373          * (could also be the top-right close button).
5374          * @param {String} title The title bar text
5375          * @param {String} msg The message box body text
5376          * @param {Function} fn (optional) The callback function invoked after the message box is closed
5377          * @param {Object} scope (optional) The scope of the callback function
5378          * @return {Roo.MessageBox} This message box
5379          */
5380         alert : function(title, msg, fn, scope)
5381         {
5382             this.show({
5383                 title : title,
5384                 msg : msg,
5385                 buttons: this.OK,
5386                 fn: fn,
5387                 closable : false,
5388                 scope : scope,
5389                 modal : true
5390             });
5391             return this;
5392         },
5393
5394         /**
5395          * Displays a message box with an infinitely auto-updating progress bar.  This can be used to block user
5396          * interaction while waiting for a long-running process to complete that does not have defined intervals.
5397          * You are responsible for closing the message box when the process is complete.
5398          * @param {String} msg The message box body text
5399          * @param {String} title (optional) The title bar text
5400          * @return {Roo.MessageBox} This message box
5401          */
5402         wait : function(msg, title){
5403             this.show({
5404                 title : title,
5405                 msg : msg,
5406                 buttons: false,
5407                 closable:false,
5408                 progress:true,
5409                 modal:true,
5410                 width:300,
5411                 wait:true
5412             });
5413             waitTimer = Roo.TaskMgr.start({
5414                 run: function(i){
5415                     Roo.MessageBox.updateProgress(((((i+20)%20)+1)*5)*.01);
5416                 },
5417                 interval: 1000
5418             });
5419             return this;
5420         },
5421
5422         /**
5423          * Displays a confirmation message box with Yes and No buttons (comparable to JavaScript's Window.confirm).
5424          * If a callback function is passed it will be called after the user clicks either button, and the id of the
5425          * button that was clicked will be passed as the only parameter to the callback (could also be the top-right close button).
5426          * @param {String} title The title bar text
5427          * @param {String} msg The message box body text
5428          * @param {Function} fn (optional) The callback function invoked after the message box is closed
5429          * @param {Object} scope (optional) The scope of the callback function
5430          * @return {Roo.MessageBox} This message box
5431          */
5432         confirm : function(title, msg, fn, scope){
5433             this.show({
5434                 title : title,
5435                 msg : msg,
5436                 buttons: this.YESNO,
5437                 fn: fn,
5438                 scope : scope,
5439                 modal : true
5440             });
5441             return this;
5442         },
5443
5444         /**
5445          * Displays a message box with OK and Cancel buttons prompting the user to enter some text (comparable to
5446          * JavaScript's Window.prompt).  The prompt can be a single-line or multi-line textbox.  If a callback function
5447          * is passed it will be called after the user clicks either button, and the id of the button that was clicked
5448          * (could also be the top-right close button) and the text that was entered will be passed as the two
5449          * parameters to the callback.
5450          * @param {String} title The title bar text
5451          * @param {String} msg The message box body text
5452          * @param {Function} fn (optional) The callback function invoked after the message box is closed
5453          * @param {Object} scope (optional) The scope of the callback function
5454          * @param {Boolean/Number} multiline (optional) True to create a multiline textbox using the defaultTextHeight
5455          * property, or the height in pixels to create the textbox (defaults to false / single-line)
5456          * @return {Roo.MessageBox} This message box
5457          */
5458         prompt : function(title, msg, fn, scope, multiline){
5459             this.show({
5460                 title : title,
5461                 msg : msg,
5462                 buttons: this.OKCANCEL,
5463                 fn: fn,
5464                 minWidth:250,
5465                 scope : scope,
5466                 prompt:true,
5467                 multiline: multiline,
5468                 modal : true
5469             });
5470             return this;
5471         },
5472
5473         /**
5474          * Button config that displays a single OK button
5475          * @type Object
5476          */
5477         OK : {ok:true},
5478         /**
5479          * Button config that displays Yes and No buttons
5480          * @type Object
5481          */
5482         YESNO : {yes:true, no:true},
5483         /**
5484          * Button config that displays OK and Cancel buttons
5485          * @type Object
5486          */
5487         OKCANCEL : {ok:true, cancel:true},
5488         /**
5489          * Button config that displays Yes, No and Cancel buttons
5490          * @type Object
5491          */
5492         YESNOCANCEL : {yes:true, no:true, cancel:true},
5493
5494         /**
5495          * The default height in pixels of the message box's multiline textarea if displayed (defaults to 75)
5496          * @type Number
5497          */
5498         defaultTextHeight : 75,
5499         /**
5500          * The maximum width in pixels of the message box (defaults to 600)
5501          * @type Number
5502          */
5503         maxWidth : 600,
5504         /**
5505          * The minimum width in pixels of the message box (defaults to 100)
5506          * @type Number
5507          */
5508         minWidth : 100,
5509         /**
5510          * The minimum width in pixels of the message box if it is a progress-style dialog.  This is useful
5511          * for setting a different minimum width than text-only dialogs may need (defaults to 250)
5512          * @type Number
5513          */
5514         minProgressWidth : 250,
5515         /**
5516          * An object containing the default button text strings that can be overriden for localized language support.
5517          * Supported properties are: ok, cancel, yes and no.
5518          * Customize the default text like so: Roo.MessageBox.buttonText.yes = "S?";
5519          * @type Object
5520          */
5521         buttonText : {
5522             ok : "OK",
5523             cancel : "Cancel",
5524             yes : "Yes",
5525             no : "No"
5526         }
5527     };
5528 }();
5529
5530 /**
5531  * Shorthand for {@link Roo.MessageBox}
5532  */
5533 Roo.MessageBox = Roo.MessageBox || Roo.bootstrap.MessageBox;
5534 Roo.Msg = Roo.Msg || Roo.MessageBox;
5535 /*
5536  * - LGPL
5537  *
5538  * navbar
5539  * 
5540  */
5541
5542 /**
5543  * @class Roo.bootstrap.nav.Bar
5544  * @extends Roo.bootstrap.Component
5545  * @abstract
5546  * Bootstrap Navbar class
5547
5548  * @constructor
5549  * Create a new Navbar
5550  * @param {Object} config The config object
5551  */
5552
5553
5554 Roo.bootstrap.nav.Bar = function(config){
5555     Roo.bootstrap.nav.Bar.superclass.constructor.call(this, config);
5556     this.addEvents({
5557         // raw events
5558         /**
5559          * @event beforetoggle
5560          * Fire before toggle the menu
5561          * @param {Roo.EventObject} e
5562          */
5563         "beforetoggle" : true
5564     });
5565 };
5566
5567 Roo.extend(Roo.bootstrap.nav.Bar, Roo.bootstrap.Component,  {
5568     
5569     
5570    
5571     // private
5572     navItems : false,
5573     loadMask : false,
5574     
5575     
5576     getAutoCreate : function(){
5577         
5578         
5579         throw { message : "nav bar is now a abstract base class - use NavSimplebar / NavHeaderbar / NavSidebar etc..."};
5580         
5581     },
5582     
5583     initEvents :function ()
5584     {
5585         //Roo.log(this.el.select('.navbar-toggle',true));
5586         this.el.select('.navbar-toggle',true).on('click', this.onToggle , this);
5587         
5588         var mark = {
5589             tag: "div",
5590             cls:"x-dlg-mask"
5591         };
5592         
5593         this.maskEl = Roo.DomHelper.append(this.el, mark, true);
5594         
5595         var size = this.el.getSize();
5596         this.maskEl.setSize(size.width, size.height);
5597         this.maskEl.enableDisplayMode("block");
5598         this.maskEl.hide();
5599         
5600         if(this.loadMask){
5601             this.maskEl.show();
5602         }
5603     },
5604     
5605     
5606     getChildContainer : function()
5607     {
5608         if (this.el && this.el.select('.collapse').getCount()) {
5609             return this.el.select('.collapse',true).first();
5610         }
5611         
5612         return this.el;
5613     },
5614     
5615     mask : function()
5616     {
5617         this.maskEl.show();
5618     },
5619     
5620     unmask : function()
5621     {
5622         this.maskEl.hide();
5623     },
5624     onToggle : function()
5625     {
5626         
5627         if(this.fireEvent('beforetoggle', this) === false){
5628             return;
5629         }
5630         var ce = this.el.select('.navbar-collapse',true).first();
5631       
5632         if (!ce.hasClass('show')) {
5633            this.expand();
5634         } else {
5635             this.collapse();
5636         }
5637         
5638         
5639     
5640     },
5641     /**
5642      * Expand the navbar pulldown 
5643      */
5644     expand : function ()
5645     {
5646        
5647         var ce = this.el.select('.navbar-collapse',true).first();
5648         if (ce.hasClass('collapsing')) {
5649             return;
5650         }
5651         ce.dom.style.height = '';
5652                // show it...
5653         ce.addClass('in'); // old...
5654         ce.removeClass('collapse');
5655         ce.addClass('show');
5656         var h = ce.getHeight();
5657         Roo.log(h);
5658         ce.removeClass('show');
5659         // at this point we should be able to see it..
5660         ce.addClass('collapsing');
5661         
5662         ce.setHeight(0); // resize it ...
5663         ce.on('transitionend', function() {
5664             //Roo.log('done transition');
5665             ce.removeClass('collapsing');
5666             ce.addClass('show');
5667             ce.removeClass('collapse');
5668
5669             ce.dom.style.height = '';
5670         }, this, { single: true} );
5671         ce.setHeight(h);
5672         ce.dom.scrollTop = 0;
5673     },
5674     /**
5675      * Collapse the navbar pulldown 
5676      */
5677     collapse : function()
5678     {
5679          var ce = this.el.select('.navbar-collapse',true).first();
5680        
5681         if (ce.hasClass('collapsing') || ce.hasClass('collapse') ) {
5682             // it's collapsed or collapsing..
5683             return;
5684         }
5685         ce.removeClass('in'); // old...
5686         ce.setHeight(ce.getHeight());
5687         ce.removeClass('show');
5688         ce.addClass('collapsing');
5689         
5690         ce.on('transitionend', function() {
5691             ce.dom.style.height = '';
5692             ce.removeClass('collapsing');
5693             ce.addClass('collapse');
5694         }, this, { single: true} );
5695         ce.setHeight(0);
5696     }
5697     
5698     
5699     
5700 });
5701
5702
5703
5704  
5705
5706  /*
5707  * - LGPL
5708  *
5709  * navbar
5710  * 
5711  */
5712
5713 /**
5714  * @class Roo.bootstrap.nav.Simplebar
5715  * @extends Roo.bootstrap.nav.Bar
5716  * @children Roo.bootstrap.nav.Group Roo.bootstrap.Container Roo.bootstrap.form.Form Roo.bootstrap.Row Roo.bootstrap.Column Roo.bootstrap.Link
5717  * Bootstrap Sidebar class
5718  *
5719  * @cfg {Boolean} inverse is inverted color
5720  * 
5721  * @cfg {String} type (nav | pills | tabs)
5722  * @cfg {Boolean} arrangement stacked | justified
5723  * @cfg {String} align (left | right) alignment
5724  * 
5725  * @cfg {Boolean} main (true|false) main nav bar? default false
5726  * @cfg {Boolean} loadMask (true|false) loadMask on the bar
5727  * 
5728  * @cfg {String} tag (header|footer|nav|div) default is nav 
5729
5730  * @cfg {String} weight (light|primary|secondary|success|danger|warning|info|dark|white) default is light.
5731  * 
5732  * 
5733  * @constructor
5734  * Create a new Sidebar
5735  * @param {Object} config The config object
5736  */
5737
5738
5739 Roo.bootstrap.nav.Simplebar = function(config){
5740     Roo.bootstrap.nav.Simplebar.superclass.constructor.call(this, config);
5741 };
5742
5743 Roo.extend(Roo.bootstrap.nav.Simplebar, Roo.bootstrap.nav.Bar,  {
5744     
5745     inverse: false,
5746     
5747     type: false,
5748     arrangement: '',
5749     align : false,
5750     
5751     weight : 'light',
5752     
5753     main : false,
5754     
5755     
5756     tag : false,
5757     
5758     
5759     getAutoCreate : function(){
5760         
5761         
5762         var cfg = {
5763             tag : this.tag || 'div',
5764             cls : 'navbar roo-navbar-simple' //navbar-expand-lg ??
5765         };
5766         if (['light','white'].indexOf(this.weight) > -1) {
5767             cfg.cls += ['light','white'].indexOf(this.weight) > -1 ? ' navbar-light' : ' navbar-dark';
5768         }
5769         cfg.cls += ' bg-' + this.weight;
5770         
5771         if (this.inverse) {
5772             cfg.cls += ' navbar-inverse';
5773             
5774         }
5775         
5776         // i'm not actually sure these are really used - normally we add a navGroup to a navbar
5777         
5778         if (Roo.bootstrap.version == 4 && this.xtype == 'NavSimplebar') {
5779             return cfg;
5780         }
5781         
5782         
5783     
5784         
5785         cfg.cn = [
5786             {
5787                 cls: 'nav nav-' + this.xtype,
5788                 tag : 'ul'
5789             }
5790         ];
5791         
5792          
5793         this.type = this.type || 'nav';
5794         if (['tabs','pills'].indexOf(this.type) != -1) {
5795             cfg.cn[0].cls += ' nav-' + this.type
5796         
5797         
5798         } else {
5799             if (this.type!=='nav') {
5800                 Roo.log('nav type must be nav/tabs/pills')
5801             }
5802             cfg.cn[0].cls += ' navbar-nav'
5803         }
5804         
5805         
5806         
5807         
5808         if (['stacked','justified'].indexOf(this.arrangement) != -1) {
5809             cfg.cn[0].cls += ' nav-' + this.arrangement;
5810         }
5811         
5812         
5813         if (this.align === 'right') {
5814             cfg.cn[0].cls += ' navbar-right';
5815         }
5816         
5817         
5818         
5819         
5820         return cfg;
5821     
5822         
5823     }
5824     
5825     
5826     
5827 });
5828
5829
5830
5831  
5832
5833  
5834        /*
5835  * - LGPL
5836  *
5837  * navbar
5838  * navbar-fixed-top
5839  * navbar-expand-md  fixed-top 
5840  */
5841
5842 /**
5843  * @class Roo.bootstrap.nav.Headerbar
5844  * @extends Roo.bootstrap.nav.Simplebar
5845  * @children Roo.bootstrap.nav.Group Roo.bootstrap.Container Roo.bootstrap.form.Form Roo.bootstrap.Row Roo.bootstrap.Column Roo.bootstrap.Link
5846  * Bootstrap Sidebar class
5847  *
5848  * @cfg {String} brand what is brand
5849  * @cfg {String} position (fixed-top|fixed-bottom|static-top) position
5850  * @cfg {String} brand_href href of the brand
5851  * @cfg {Boolean} srButton generate the (screen reader / mobile) sr-only button   default true
5852  * @cfg {Boolean} autohide a top nav bar header that hides on scroll.
5853  * @cfg {Boolean} desktopCenter should the header be centered on desktop using a container class
5854  * @cfg {Roo.bootstrap.Row} mobilerow - a row to display on mobile only..
5855  * 
5856  * @constructor
5857  * Create a new Sidebar
5858  * @param {Object} config The config object
5859  */
5860
5861
5862 Roo.bootstrap.nav.Headerbar = function(config){
5863     Roo.bootstrap.nav.Headerbar.superclass.constructor.call(this, config);
5864       
5865 };
5866
5867 Roo.extend(Roo.bootstrap.nav.Headerbar, Roo.bootstrap.nav.Simplebar,  {
5868     
5869     position: '',
5870     brand: '',
5871     brand_href: false,
5872     srButton : true,
5873     autohide : false,
5874     desktopCenter : false,
5875    
5876     
5877     getAutoCreate : function(){
5878         
5879         var   cfg = {
5880             tag: this.nav || 'nav',
5881             cls: 'navbar navbar-expand-md',
5882             role: 'navigation',
5883             cn: []
5884         };
5885         
5886         var cn = cfg.cn;
5887         if (this.desktopCenter) {
5888             cn.push({cls : 'container', cn : []});
5889             cn = cn[0].cn;
5890         }
5891         
5892         if(this.srButton){
5893             var btn = {
5894                 tag: 'button',
5895                 type: 'button',
5896                 cls: 'navbar-toggle navbar-toggler',
5897                 'data-toggle': 'collapse',
5898                 cn: [
5899                     {
5900                         tag: 'span',
5901                         cls: 'sr-only',
5902                         html: 'Toggle navigation'
5903                     },
5904                     {
5905                         tag: 'span',
5906                         cls: 'icon-bar navbar-toggler-icon'
5907                     },
5908                     {
5909                         tag: 'span',
5910                         cls: 'icon-bar'
5911                     },
5912                     {
5913                         tag: 'span',
5914                         cls: 'icon-bar'
5915                     }
5916                 ]
5917             };
5918             
5919             cn.push( Roo.bootstrap.version == 4 ? btn : {
5920                 tag: 'div',
5921                 cls: 'navbar-header',
5922                 cn: [
5923                     btn
5924                 ]
5925             });
5926         }
5927         
5928         cn.push({
5929             tag: 'div',
5930             cls: Roo.bootstrap.version == 4  ? 'nav flex-row roo-navbar-collapse collapse navbar-collapse' : 'collapse navbar-collapse roo-navbar-collapse',
5931             cn : []
5932         });
5933         
5934         cfg.cls += this.inverse ? ' navbar-inverse navbar-dark bg-dark' : ' navbar-default';
5935         
5936         if (['light','white'].indexOf(this.weight) > -1) {
5937             cfg.cls += ['light','white'].indexOf(this.weight) > -1 ? ' navbar-light' : ' navbar-dark';
5938         }
5939         cfg.cls += ' bg-' + this.weight;
5940         
5941         
5942         if (['fixed-top','fixed-bottom','static-top'].indexOf(this.position)>-1) {
5943             cfg.cls += ' navbar-' + this.position + ' ' + this.position ;
5944             
5945             // tag can override this..
5946             
5947             cfg.tag = this.tag || (this.position  == 'fixed-bottom' ? 'footer' : 'header');
5948         }
5949         
5950         if (this.brand !== '') {
5951             var cp =  Roo.bootstrap.version == 4 ? cn : cn[0].cn;
5952             cp.unshift({ // changed from push ?? BS4 needs it at the start? - does this break or exsiting?
5953                 tag: 'a',
5954                 href: this.brand_href ? this.brand_href : '#',
5955                 cls: 'navbar-brand',
5956                 cn: [
5957                 this.brand
5958                 ]
5959             });
5960         }
5961         
5962         if(this.main){
5963             cfg.cls += ' main-nav';
5964         }
5965         
5966         
5967         return cfg;
5968
5969         
5970     },
5971     getHeaderChildContainer : function()
5972     {
5973         if (this.srButton && this.el.select('.navbar-header').getCount()) {
5974             return this.el.select('.navbar-header',true).first();
5975         }
5976         
5977         return this.getChildContainer();
5978     },
5979     
5980     getChildContainer : function()
5981     {
5982          
5983         return this.el.select('.roo-navbar-collapse',true).first();
5984          
5985         
5986     },
5987     
5988     initEvents : function()
5989     {
5990         Roo.bootstrap.nav.Headerbar.superclass.initEvents.call(this);
5991         
5992         if (this.autohide) {
5993             
5994             var prevScroll = 0;
5995             var ft = this.el;
5996             
5997             Roo.get(document).on('scroll',function(e) {
5998                 var ns = Roo.get(document).getScroll().top;
5999                 var os = prevScroll;
6000                 prevScroll = ns;
6001                 
6002                 if(ns > os){
6003                     ft.removeClass('slideDown');
6004                     ft.addClass('slideUp');
6005                     return;
6006                 }
6007                 ft.removeClass('slideUp');
6008                 ft.addClass('slideDown');
6009                  
6010               
6011           },this);
6012         }
6013     }    
6014     
6015 });
6016
6017
6018
6019  
6020
6021  /*
6022  * - LGPL
6023  *
6024  * navbar
6025  * 
6026  */
6027
6028 /**
6029  * @class Roo.bootstrap.nav.Sidebar
6030  * @extends Roo.bootstrap.nav.Bar
6031  * @children Roo.bootstrap.nav.Group Roo.bootstrap.Container Roo.bootstrap.form.Form Roo.bootstrap.Row Roo.bootstrap.Column Roo.bootstrap.Link
6032  * Bootstrap Sidebar class
6033  * 
6034  * @constructor
6035  * Create a new Sidebar
6036  * @param {Object} config The config object
6037  */
6038
6039
6040 Roo.bootstrap.nav.Sidebar = function(config){
6041     Roo.bootstrap.nav.Sidebar.superclass.constructor.call(this, config);
6042 };
6043
6044 Roo.extend(Roo.bootstrap.nav.Sidebar, Roo.bootstrap.nav.Bar,  {
6045     
6046     sidebar : true, // used by Navbar Item and NavbarGroup at present...
6047     
6048     getAutoCreate : function(){
6049         
6050         
6051         return  {
6052             tag: 'div',
6053             cls: 'sidebar sidebar-nav'
6054         };
6055     
6056         
6057     }
6058     
6059     
6060     
6061 });
6062
6063
6064
6065  
6066
6067  /*
6068  * - LGPL
6069  *
6070  * nav group
6071  * 
6072  */
6073
6074 /**
6075  * @class Roo.bootstrap.nav.Group
6076  * @extends Roo.bootstrap.Component
6077  * @children Roo.bootstrap.nav.Item
6078  * Bootstrap NavGroup class
6079  * @cfg {String} align (left|right)
6080  * @cfg {Boolean} inverse
6081  * @cfg {String} type (nav|pills|tab) default nav
6082  * @cfg {String} navId - reference Id for navbar.
6083  * @cfg {Boolean} pilltype default true (turn to off to disable active toggle)
6084  * 
6085  * @constructor
6086  * Create a new nav group
6087  * @param {Object} config The config object
6088  */
6089
6090 Roo.bootstrap.nav.Group = function(config){
6091     Roo.bootstrap.nav.Group.superclass.constructor.call(this, config);
6092     this.navItems = [];
6093    
6094     Roo.bootstrap.nav.Group.register(this);
6095      this.addEvents({
6096         /**
6097              * @event changed
6098              * Fires when the active item changes
6099              * @param {Roo.bootstrap.nav.Group} this
6100              * @param {Roo.bootstrap.Navbar.Item} selected The item selected
6101              * @param {Roo.bootstrap.Navbar.Item} prev The previously selected item 
6102          */
6103         'changed': true
6104      });
6105     
6106 };
6107
6108 Roo.extend(Roo.bootstrap.nav.Group, Roo.bootstrap.Component,  {
6109     
6110     align: '',
6111     inverse: false,
6112     form: false,
6113     type: 'nav',
6114     navId : '',
6115     // private
6116     pilltype : true,
6117     
6118     navItems : false, 
6119     
6120     getAutoCreate : function()
6121     {
6122         var cfg = Roo.apply({}, Roo.bootstrap.nav.Group.superclass.getAutoCreate.call(this));
6123         
6124         cfg = {
6125             tag : 'ul',
6126             cls: 'nav' 
6127         };
6128         if (Roo.bootstrap.version == 4) {
6129             if (['tabs','pills'].indexOf(this.type) != -1) {
6130                 cfg.cls += ' nav-' + this.type; 
6131             } else {
6132                 // trying to remove so header bar can right align top?
6133                 if (this.parent() && this.parent().xtype != 'NavHeaderbar') {
6134                     // do not use on header bar... 
6135                     cfg.cls += ' navbar-nav';
6136                 }
6137             }
6138             
6139         } else {
6140             if (['tabs','pills'].indexOf(this.type) != -1) {
6141                 cfg.cls += ' nav-' + this.type
6142             } else {
6143                 if (this.type !== 'nav') {
6144                     Roo.log('nav type must be nav/tabs/pills')
6145                 }
6146                 cfg.cls += ' navbar-nav'
6147             }
6148         }
6149         
6150         if (this.parent() && this.parent().sidebar) {
6151             cfg = {
6152                 tag: 'ul',
6153                 cls: 'dashboard-menu sidebar-menu'
6154             };
6155             
6156             return cfg;
6157         }
6158         
6159         if (this.form === true) {
6160             cfg = {
6161                 tag: 'form',
6162                 cls: 'navbar-form form-inline'
6163             };
6164             //nav navbar-right ml-md-auto
6165             if (this.align === 'right') {
6166                 cfg.cls += ' navbar-right ml-md-auto';
6167             } else {
6168                 cfg.cls += ' navbar-left';
6169             }
6170         }
6171         
6172         if (this.align === 'right') {
6173             cfg.cls += ' navbar-right ml-md-auto';
6174         } else {
6175             cfg.cls += ' mr-auto';
6176         }
6177         
6178         if (this.inverse) {
6179             cfg.cls += ' navbar-inverse';
6180             
6181         }
6182         
6183         
6184         return cfg;
6185     },
6186     /**
6187     * sets the active Navigation item
6188     * @param {Roo.bootstrap.nav.Item} the new current navitem
6189     */
6190     setActiveItem : function(item)
6191     {
6192         var prev = false;
6193         Roo.each(this.navItems, function(v){
6194             if (v == item) {
6195                 return ;
6196             }
6197             if (v.isActive()) {
6198                 v.setActive(false, true);
6199                 prev = v;
6200                 
6201             }
6202             
6203         });
6204
6205         item.setActive(true, true);
6206         this.fireEvent('changed', this, item, prev);
6207         
6208         
6209     },
6210     /**
6211     * gets the active Navigation item
6212     * @return {Roo.bootstrap.nav.Item} the current navitem
6213     */
6214     getActive : function()
6215     {
6216         
6217         var prev = false;
6218         Roo.each(this.navItems, function(v){
6219             
6220             if (v.isActive()) {
6221                 prev = v;
6222                 
6223             }
6224             
6225         });
6226         return prev;
6227     },
6228     
6229     indexOfNav : function()
6230     {
6231         
6232         var prev = false;
6233         Roo.each(this.navItems, function(v,i){
6234             
6235             if (v.isActive()) {
6236                 prev = i;
6237                 
6238             }
6239             
6240         });
6241         return prev;
6242     },
6243     /**
6244     * adds a Navigation item
6245     * @param {Roo.bootstrap.nav.Item} the navitem to add
6246     */
6247     addItem : function(cfg)
6248     {
6249         if (this.form && Roo.bootstrap.version == 4) {
6250             cfg.tag = 'div';
6251         }
6252         var cn = new Roo.bootstrap.nav.Item(cfg);
6253         this.register(cn);
6254         cn.parentId = this.id;
6255         cn.onRender(this.el, null);
6256         return cn;
6257     },
6258     /**
6259     * register a Navigation item
6260     * @param {Roo.bootstrap.nav.Item} the navitem to add
6261     */
6262     register : function(item)
6263     {
6264         this.navItems.push( item);
6265         item.navId = this.navId;
6266     
6267     },
6268     
6269     /**
6270     * clear all the Navigation item
6271     */
6272    
6273     clearAll : function()
6274     {
6275         this.navItems = [];
6276         this.el.dom.innerHTML = '';
6277     },
6278     
6279     getNavItem: function(tabId)
6280     {
6281         var ret = false;
6282         Roo.each(this.navItems, function(e) {
6283             if (e.tabId == tabId) {
6284                ret =  e;
6285                return false;
6286             }
6287             return true;
6288             
6289         });
6290         return ret;
6291     },
6292     
6293     setActiveNext : function()
6294     {
6295         var i = this.indexOfNav(this.getActive());
6296         if (i > this.navItems.length) {
6297             return;
6298         }
6299         this.setActiveItem(this.navItems[i+1]);
6300     },
6301     setActivePrev : function()
6302     {
6303         var i = this.indexOfNav(this.getActive());
6304         if (i  < 1) {
6305             return;
6306         }
6307         this.setActiveItem(this.navItems[i-1]);
6308     },
6309     clearWasActive : function(except) {
6310         Roo.each(this.navItems, function(e) {
6311             if (e.tabId != except.tabId && e.was_active) {
6312                e.was_active = false;
6313                return false;
6314             }
6315             return true;
6316             
6317         });
6318     },
6319     getWasActive : function ()
6320     {
6321         var r = false;
6322         Roo.each(this.navItems, function(e) {
6323             if (e.was_active) {
6324                r = e;
6325                return false;
6326             }
6327             return true;
6328             
6329         });
6330         return r;
6331     }
6332     
6333     
6334 });
6335
6336  
6337 Roo.apply(Roo.bootstrap.nav.Group, {
6338     
6339     groups: {},
6340      /**
6341     * register a Navigation Group
6342     * @param {Roo.bootstrap.nav.Group} the navgroup to add
6343     */
6344     register : function(navgrp)
6345     {
6346         this.groups[navgrp.navId] = navgrp;
6347         
6348     },
6349     /**
6350     * fetch a Navigation Group based on the navigation ID
6351     * @param {string} the navgroup to add
6352     * @returns {Roo.bootstrap.nav.Group} the navgroup 
6353     */
6354     get: function(navId) {
6355         if (typeof(this.groups[navId]) == 'undefined') {
6356             return false;
6357             //this.register(new Roo.bootstrap.nav.Group({ navId : navId }));
6358         }
6359         return this.groups[navId] ;
6360     }
6361     
6362     
6363     
6364 });
6365
6366  /**
6367  * @class Roo.bootstrap.nav.Item
6368  * @extends Roo.bootstrap.Component
6369  * @children Roo.bootstrap.Container Roo.bootstrap.Button
6370  * @parent Roo.bootstrap.nav.Group
6371  * @licence LGPL
6372  * Bootstrap Navbar.NavItem class
6373  * 
6374  * @cfg {String} href  link to
6375  * @cfg {String} button_weight (default|primary|secondary|success|info|warning|danger|link|light|dark) default none
6376  * @cfg {Boolean} button_outline show and outlined button
6377  * @cfg {String} html content of button
6378  * @cfg {String} badge text inside badge
6379  * @cfg {String} badgecls (bg-green|bg-red|bg-yellow)the extra classes for the badge
6380  * @cfg {String} glyphicon DEPRICATED - use fa
6381  * @cfg {String} icon DEPRICATED - use fa
6382  * @cfg {String} fa - Fontawsome icon name (can add stuff to it like fa-2x)
6383  * @cfg {Boolean} active Is item active
6384  * @cfg {Boolean} disabled Is item disabled
6385  * @cfg {String} linkcls  Link Class
6386  * @cfg {Boolean} preventDefault (true | false) default false
6387  * @cfg {String} tabId the tab that this item activates.
6388  * @cfg {String} tagtype (a|span) render as a href or span?
6389  * @cfg {Boolean} animateRef (true|false) link to element default false  
6390  * @cfg {Roo.bootstrap.menu.Menu} menu a Menu 
6391   
6392  * @constructor
6393  * Create a new Navbar Item
6394  * @param {Object} config The config object
6395  */
6396 Roo.bootstrap.nav.Item = function(config){
6397     Roo.bootstrap.nav.Item.superclass.constructor.call(this, config);
6398     this.addEvents({
6399         // raw events
6400         /**
6401          * @event click
6402          * The raw click event for the entire grid.
6403          * @param {Roo.EventObject} e
6404          */
6405         "click" : true,
6406          /**
6407             * @event changed
6408             * Fires when the active item active state changes
6409             * @param {Roo.bootstrap.nav.Item} this
6410             * @param {boolean} state the new state
6411              
6412          */
6413         'changed': true,
6414         /**
6415             * @event scrollto
6416             * Fires when scroll to element
6417             * @param {Roo.bootstrap.nav.Item} this
6418             * @param {Object} options
6419             * @param {Roo.EventObject} e
6420              
6421          */
6422         'scrollto': true
6423     });
6424    
6425 };
6426
6427 Roo.extend(Roo.bootstrap.nav.Item, Roo.bootstrap.Component,  {
6428     
6429     href: false,
6430     html: '',
6431     badge: '',
6432     icon: false,
6433     fa : false,
6434     glyphicon: false,
6435     active: false,
6436     preventDefault : false,
6437     tabId : false,
6438     tagtype : 'a',
6439     tag: 'li',
6440     disabled : false,
6441     animateRef : false,
6442     was_active : false,
6443     button_weight : '',
6444     button_outline : false,
6445     linkcls : '',
6446     navLink: false,
6447     
6448     getAutoCreate : function(){
6449          
6450         var cfg = {
6451             tag: this.tag,
6452             cls: 'nav-item'
6453         };
6454         
6455         cfg.cls =  typeof(cfg.cls) == 'undefined'  ? '' : cfg.cls;
6456         
6457         if (this.active) {
6458             cfg.cls +=  ' active' ;
6459         }
6460         if (this.disabled) {
6461             cfg.cls += ' disabled';
6462         }
6463         
6464         // BS4 only?
6465         if (this.button_weight.length) {
6466             cfg.tag = this.href ? 'a' : 'button';
6467             cfg.html = this.html || '';
6468             cfg.cls += ' btn btn' + (this.button_outline ? '-outline' : '') + '-' + this.button_weight;
6469             if (this.href) {
6470                 cfg.href = this.href;
6471             }
6472             if (this.fa) {
6473                 cfg.html = '<i class="fa fas fa-'+this.fa+'"></i> <span class="nav-html">' + this.html + '</span>';
6474             } else {
6475                 cfg.cls += " nav-html";
6476             }
6477             
6478             // menu .. should add dropdown-menu class - so no need for carat..
6479             
6480             if (this.badge !== '') {
6481                  
6482                 cfg.html += ' <span class="badge badge-secondary">' + this.badge + '</span>';
6483             }
6484             return cfg;
6485         }
6486         
6487         if (this.href || this.html || this.glyphicon || this.icon || this.fa) {
6488             cfg.cn = [
6489                 {
6490                     tag: this.tagtype,
6491                     href : this.href || "#",
6492                     html: this.html || '',
6493                     cls : ''
6494                 }
6495             ];
6496             if (this.tagtype == 'a') {
6497                 cfg.cn[0].cls = 'nav-link' +  (this.active ?  ' active'  : '') + ' ' + this.linkcls;
6498         
6499             }
6500             if (this.icon) {
6501                 cfg.cn[0].html = '<i class="'+this.icon+'"></i> <span class="nav-html">' + cfg.cn[0].html + '</span>';
6502             } else  if (this.fa) {
6503                 cfg.cn[0].html = '<i class="fa fas fa-'+this.fa+'"></i> <span class="nav-html">' + cfg.cn[0].html + '</span>';
6504             } else if(this.glyphicon) {
6505                 cfg.cn[0].html = '<span class="glyphicon glyphicon-' + this.glyphicon + '"></span> '  + cfg.cn[0].html;
6506             } else {
6507                 cfg.cn[0].cls += " nav-html";
6508             }
6509             
6510             if (this.menu) {
6511                 cfg.cn[0].html += " <span class='caret'></span>";
6512              
6513             }
6514             
6515             if (this.badge !== '') {
6516                 cfg.cn[0].html += ' <span class="badge badge-secondary">' + this.badge + '</span>';
6517             }
6518         }
6519         
6520         
6521         
6522         return cfg;
6523     },
6524     onRender : function(ct, position)
6525     {
6526        // Roo.log("Call onRender: " + this.xtype);
6527         if (Roo.bootstrap.version == 4 && ct.dom.type != 'ul') {
6528             this.tag = 'div';
6529         }
6530         
6531         var ret = Roo.bootstrap.nav.Item.superclass.onRender.call(this, ct, position);
6532         this.navLink = this.el.select('.nav-link',true).first();
6533         this.htmlEl = this.el.hasClass('nav-html') ? this.el : this.el.select('.nav-html',true).first();
6534         return ret;
6535     },
6536       
6537     
6538     initEvents: function() 
6539     {
6540         if (typeof (this.menu) != 'undefined') {
6541             this.menu.parentType = this.xtype;
6542             this.menu.triggerEl = this.el;
6543             this.menu = this.addxtype(Roo.apply({}, this.menu));
6544         }
6545         
6546         this.el.on('click', this.onClick, this);
6547         
6548         //if(this.tagtype == 'span'){
6549         //    this.el.select('span',true).on('click', this.onClick, this);
6550         //}
6551        
6552         // at this point parent should be available..
6553         this.parent().register(this);
6554     },
6555     
6556     onClick : function(e)
6557     {
6558         if (e.getTarget('.dropdown-menu-item')) {
6559             // did you click on a menu itemm.... - then don't trigger onclick..
6560             return;
6561         }
6562         
6563         if(
6564                 this.preventDefault ||
6565                                 this.href === false ||
6566                 this.href === '#' 
6567         ){
6568             //Roo.log("NavItem - prevent Default?");
6569             e.preventDefault();
6570         }
6571         
6572         if (this.disabled) {
6573             return;
6574         }
6575         
6576         var tg = Roo.bootstrap.TabGroup.get(this.navId);
6577         if (tg && tg.transition) {
6578             Roo.log("waiting for the transitionend");
6579             return;
6580         }
6581         
6582         
6583         
6584         //Roo.log("fire event clicked");
6585         if(this.fireEvent('click', this, e) === false){
6586             return;
6587         };
6588         
6589         if(this.tagtype == 'span'){
6590             return;
6591         }
6592         
6593         //Roo.log(this.href);
6594         var ael = this.el.select('a',true).first();
6595         //Roo.log(ael);
6596         
6597         if(ael && this.animateRef && this.href.indexOf('#') > -1){
6598             //Roo.log(["test:",ael.dom.href.split("#")[0], document.location.toString().split("#")[0]]);
6599             if (ael.dom.href.split("#")[0] != document.location.toString().split("#")[0]) {
6600                 return; // ignore... - it's a 'hash' to another page.
6601             }
6602             Roo.log("NavItem - prevent Default?");
6603             e.preventDefault();
6604             this.scrollToElement(e);
6605         }
6606         
6607         
6608         var p =  this.parent();
6609    
6610         if (['tabs','pills'].indexOf(p.type)!==-1 && p.pilltype) {
6611             if (typeof(p.setActiveItem) !== 'undefined') {
6612                 p.setActiveItem(this);
6613             }
6614         }
6615         
6616         // if parent is a navbarheader....- and link is probably a '#' page ref.. then remove the expanded menu.
6617         if (p.parentType == 'NavHeaderbar' && !this.menu) {
6618             // remove the collapsed menu expand...
6619             p.parent().el.select('.roo-navbar-collapse',true).removeClass('in');  
6620         }
6621     },
6622     
6623     isActive: function () {
6624         return this.active
6625     },
6626     setActive : function(state, fire, is_was_active)
6627     {
6628         if (this.active && !state && this.navId) {
6629             this.was_active = true;
6630             var nv = Roo.bootstrap.nav.Group.get(this.navId);
6631             if (nv) {
6632                 nv.clearWasActive(this);
6633             }
6634             
6635         }
6636         this.active = state;
6637         
6638         if (!state ) {
6639             this.el.removeClass('active');
6640             this.navLink ? this.navLink.removeClass('active') : false;
6641         } else if (!this.el.hasClass('active')) {
6642             
6643             this.el.addClass('active');
6644             if (Roo.bootstrap.version == 4 && this.navLink ) {
6645                 this.navLink.addClass('active');
6646             }
6647             
6648         }
6649         if (fire) {
6650             this.fireEvent('changed', this, state);
6651         }
6652         
6653         // show a panel if it's registered and related..
6654         
6655         if (!this.navId || !this.tabId || !state || is_was_active) {
6656             return;
6657         }
6658         
6659         var tg = Roo.bootstrap.TabGroup.get(this.navId);
6660         if (!tg) {
6661             return;
6662         }
6663         var pan = tg.getPanelByName(this.tabId);
6664         if (!pan) {
6665             return;
6666         }
6667         // if we can not flip to new panel - go back to old nav highlight..
6668         if (false == tg.showPanel(pan)) {
6669             var nv = Roo.bootstrap.nav.Group.get(this.navId);
6670             if (nv) {
6671                 var onav = nv.getWasActive();
6672                 if (onav) {
6673                     onav.setActive(true, false, true);
6674                 }
6675             }
6676             
6677         }
6678         
6679         
6680         
6681     },
6682      // this should not be here...
6683     setDisabled : function(state)
6684     {
6685         this.disabled = state;
6686         if (!state ) {
6687             this.el.removeClass('disabled');
6688         } else if (!this.el.hasClass('disabled')) {
6689             this.el.addClass('disabled');
6690         }
6691         
6692     },
6693     
6694     /**
6695      * Fetch the element to display the tooltip on.
6696      * @return {Roo.Element} defaults to this.el
6697      */
6698     tooltipEl : function()
6699     {
6700         return this.el; //this.tagtype  == 'a' ? this.el  : this.el.select('' + this.tagtype + '', true).first();
6701     },
6702     
6703     scrollToElement : function(e)
6704     {
6705         var c = document.body;
6706         
6707         /*
6708          * Firefox / IE places the overflow at the html level, unless specifically styled to behave differently.
6709          */
6710         if(Roo.isFirefox || Roo.isIE || Roo.isIE11){
6711             c = document.documentElement;
6712         }
6713         
6714         var target = Roo.get(c).select('a[name=' + this.href.split('#')[1] +']', true).first();
6715         
6716         if(!target){
6717             return;
6718         }
6719
6720         var o = target.calcOffsetsTo(c);
6721         
6722         var options = {
6723             target : target,
6724             value : o[1]
6725         };
6726         
6727         this.fireEvent('scrollto', this, options, e);
6728         
6729         Roo.get(c).scrollTo('top', options.value, true);
6730         
6731         return;
6732     },
6733     /**
6734      * Set the HTML (text content) of the item
6735      * @param {string} html  content for the nav item
6736      */
6737     setHtml : function(html)
6738     {
6739         this.html = html;
6740         this.htmlEl.dom.innerHTML = html;
6741         
6742     } 
6743 });
6744  
6745
6746  /*
6747  * - LGPL
6748  *
6749  * sidebar item
6750  *
6751  *  li
6752  *    <span> icon </span>
6753  *    <span> text </span>
6754  *    <span>badge </span>
6755  */
6756
6757 /**
6758  * @class Roo.bootstrap.nav.SidebarItem
6759  * @extends Roo.bootstrap.nav.Item
6760  * Bootstrap Navbar.NavSidebarItem class
6761  * 
6762  * {String} badgeWeight (default|primary|success|info|warning|danger)the extra classes for the badge
6763  * {Boolean} open is the menu open
6764  * {Boolean} buttonView use button as the tigger el rather that a (default false)
6765  * {String} buttonWeight (default|primary|success|info|warning|danger)the extra classes for the button
6766  * {String} buttonSize (sm|md|lg)the extra classes for the button
6767  * {Boolean} showArrow show arrow next to the text (default true)
6768  * @constructor
6769  * Create a new Navbar Button
6770  * @param {Object} config The config object
6771  */
6772 Roo.bootstrap.nav.SidebarItem = function(config){
6773     Roo.bootstrap.nav.SidebarItem.superclass.constructor.call(this, config);
6774     this.addEvents({
6775         // raw events
6776         /**
6777          * @event click
6778          * The raw click event for the entire grid.
6779          * @param {Roo.EventObject} e
6780          */
6781         "click" : true,
6782          /**
6783             * @event changed
6784             * Fires when the active item active state changes
6785             * @param {Roo.bootstrap.nav.SidebarItem} this
6786             * @param {boolean} state the new state
6787              
6788          */
6789         'changed': true
6790     });
6791    
6792 };
6793
6794 Roo.extend(Roo.bootstrap.nav.SidebarItem, Roo.bootstrap.nav.Item,  {
6795     
6796     badgeWeight : 'default',
6797     
6798     open: false,
6799     
6800     buttonView : false,
6801     
6802     buttonWeight : 'default',
6803     
6804     buttonSize : 'md',
6805     
6806     showArrow : true,
6807     
6808     getAutoCreate : function(){
6809         
6810         
6811         var a = {
6812                 tag: 'a',
6813                 href : this.href || '#',
6814                 cls: '',
6815                 html : '',
6816                 cn : []
6817         };
6818         
6819         if(this.buttonView){
6820             a = {
6821                 tag: 'button',
6822                 href : this.href || '#',
6823                 cls: 'btn btn-' + this.buttonWeight + ' btn-' + this.buttonSize + 'roo-button-dropdown-toggle',
6824                 html : this.html,
6825                 cn : []
6826             };
6827         }
6828         
6829         var cfg = {
6830             tag: 'li',
6831             cls: '',
6832             cn: [ a ]
6833         };
6834         
6835         if (this.active) {
6836             cfg.cls += ' active';
6837         }
6838         
6839         if (this.disabled) {
6840             cfg.cls += ' disabled';
6841         }
6842         if (this.open) {
6843             cfg.cls += ' open x-open';
6844         }
6845         // left icon..
6846         if (this.glyphicon || this.icon) {
6847             var c = this.glyphicon  ? ('glyphicon glyphicon-'+this.glyphicon)  : this.icon;
6848             a.cn.push({ tag : 'i', cls : c }) ;
6849         }
6850         
6851         if(!this.buttonView){
6852             var span = {
6853                 tag: 'span',
6854                 html : this.html || ''
6855             };
6856
6857             a.cn.push(span);
6858             
6859         }
6860         
6861         if (this.badge !== '') {
6862             a.cn.push({ tag: 'span',  cls : 'badge pull-right badge-' + this.badgeWeight, html: this.badge }); 
6863         }
6864         
6865         if (this.menu) {
6866             
6867             if(this.showArrow){
6868                 a.cn.push({ tag : 'i', cls : 'glyphicon glyphicon-chevron-down pull-right'});
6869             }
6870             
6871             a.cls += ' dropdown-toggle treeview' ;
6872         }
6873         
6874         return cfg;
6875     },
6876     
6877     initEvents : function()
6878     { 
6879         if (typeof (this.menu) != 'undefined') {
6880             this.menu.parentType = this.xtype;
6881             this.menu.triggerEl = this.el;
6882             this.menu = this.addxtype(Roo.apply({}, this.menu));
6883         }
6884         
6885         this.el.on('click', this.onClick, this);
6886         
6887         if(this.badge !== ''){
6888             this.badgeEl = this.el.select('.badge', true).first().setVisibilityMode(Roo.Element.DISPLAY);
6889         }
6890         
6891     },
6892     
6893     onClick : function(e)
6894     {
6895         if(this.disabled){
6896             e.preventDefault();
6897             return;
6898         }
6899         
6900         if(this.preventDefault){
6901             e.preventDefault();
6902         }
6903         
6904         this.fireEvent('click', this, e);
6905     },
6906     
6907     disable : function()
6908     {
6909         this.setDisabled(true);
6910     },
6911     
6912     enable : function()
6913     {
6914         this.setDisabled(false);
6915     },
6916     
6917     setDisabled : function(state)
6918     {
6919         if(this.disabled == state){
6920             return;
6921         }
6922         
6923         this.disabled = state;
6924         
6925         if (state) {
6926             this.el.addClass('disabled');
6927             return;
6928         }
6929         
6930         this.el.removeClass('disabled');
6931         
6932         return;
6933     },
6934     
6935     setActive : function(state)
6936     {
6937         if(this.active == state){
6938             return;
6939         }
6940         
6941         this.active = state;
6942         
6943         if (state) {
6944             this.el.addClass('active');
6945             return;
6946         }
6947         
6948         this.el.removeClass('active');
6949         
6950         return;
6951     },
6952     
6953     isActive: function () 
6954     {
6955         return this.active;
6956     },
6957     
6958     setBadge : function(str)
6959     {
6960         if(!this.badgeEl){
6961             return;
6962         }
6963         
6964         this.badgeEl.dom.innerHTML = str;
6965     }
6966     
6967    
6968      
6969  
6970 });
6971  
6972
6973  /*
6974  * - LGPL
6975  *
6976  * nav progress bar
6977  * 
6978  */
6979
6980 /**
6981  * @class Roo.bootstrap.nav.ProgressBar
6982  * @extends Roo.bootstrap.Component
6983  * @children Roo.bootstrap.nav.ProgressBarItem
6984  * Bootstrap NavProgressBar class
6985  * 
6986  * @constructor
6987  * Create a new nav progress bar - a bar indicating step along a process
6988  * @param {Object} config The config object
6989  */
6990
6991 Roo.bootstrap.nav.ProgressBar = function(config){
6992     Roo.bootstrap.nav.ProgressBar.superclass.constructor.call(this, config);
6993
6994     this.bullets = this.bullets || [];
6995    
6996 //    Roo.bootstrap.nav.ProgressBar.register(this);
6997      this.addEvents({
6998         /**
6999              * @event changed
7000              * Fires when the active item changes
7001              * @param {Roo.bootstrap.nav.ProgressBar} this
7002              * @param {Roo.bootstrap.nav.ProgressItem} selected The item selected
7003              * @param {Roo.bootstrap.nav.ProgressItem} prev The previously selected item 
7004          */
7005         'changed': true
7006      });
7007     
7008 };
7009
7010 Roo.extend(Roo.bootstrap.nav.ProgressBar, Roo.bootstrap.Component,  {
7011     /**
7012      * @cfg {Roo.bootstrap.nav.ProgressItem} NavProgressBar:bullets[]
7013      * Bullets for the Nav Progress bar for the toolbar
7014      */
7015     bullets : [],
7016     barItems : [],
7017     
7018     getAutoCreate : function()
7019     {
7020         var cfg = Roo.apply({}, Roo.bootstrap.nav.ProgressBar.superclass.getAutoCreate.call(this));
7021         
7022         cfg = {
7023             tag : 'div',
7024             cls : 'roo-navigation-bar-group',
7025             cn : [
7026                 {
7027                     tag : 'div',
7028                     cls : 'roo-navigation-top-bar'
7029                 },
7030                 {
7031                     tag : 'div',
7032                     cls : 'roo-navigation-bullets-bar',
7033                     cn : [
7034                         {
7035                             tag : 'ul',
7036                             cls : 'roo-navigation-bar'
7037                         }
7038                     ]
7039                 },
7040                 
7041                 {
7042                     tag : 'div',
7043                     cls : 'roo-navigation-bottom-bar'
7044                 }
7045             ]
7046             
7047         };
7048         
7049         return cfg;
7050         
7051     },
7052     
7053     initEvents: function() 
7054     {
7055         
7056     },
7057     
7058     onRender : function(ct, position) 
7059     {
7060         Roo.bootstrap.nav.ProgressBar.superclass.onRender.call(this, ct, position);
7061         
7062         if(this.bullets.length){
7063             Roo.each(this.bullets, function(b){
7064                this.addItem(b);
7065             }, this);
7066         }
7067         
7068         this.format();
7069         
7070     },
7071     
7072     addItem : function(cfg)
7073     {
7074         var item = new Roo.bootstrap.nav.ProgressItem(cfg);
7075         
7076         item.parentId = this.id;
7077         item.render(this.el.select('.roo-navigation-bar', true).first(), null);
7078         
7079         if(cfg.html){
7080             var top = new Roo.bootstrap.Element({
7081                 tag : 'div',
7082                 cls : 'roo-navigation-bar-text'
7083             });
7084             
7085             var bottom = new Roo.bootstrap.Element({
7086                 tag : 'div',
7087                 cls : 'roo-navigation-bar-text'
7088             });
7089             
7090             top.onRender(this.el.select('.roo-navigation-top-bar', true).first(), null);
7091             bottom.onRender(this.el.select('.roo-navigation-bottom-bar', true).first(), null);
7092             
7093             var topText = new Roo.bootstrap.Element({
7094                 tag : 'span',
7095                 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? cfg.html : ''
7096             });
7097             
7098             var bottomText = new Roo.bootstrap.Element({
7099                 tag : 'span',
7100                 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? '' : cfg.html
7101             });
7102             
7103             topText.onRender(top.el, null);
7104             bottomText.onRender(bottom.el, null);
7105             
7106             item.topEl = top;
7107             item.bottomEl = bottom;
7108         }
7109         
7110         this.barItems.push(item);
7111         
7112         return item;
7113     },
7114     
7115     getActive : function()
7116     {
7117         var active = false;
7118         
7119         Roo.each(this.barItems, function(v){
7120             
7121             if (!v.isActive()) {
7122                 return;
7123             }
7124             
7125             active = v;
7126             return false;
7127             
7128         });
7129         
7130         return active;
7131     },
7132     
7133     setActiveItem : function(item)
7134     {
7135         var prev = false;
7136         
7137         Roo.each(this.barItems, function(v){
7138             if (v.rid == item.rid) {
7139                 return ;
7140             }
7141             
7142             if (v.isActive()) {
7143                 v.setActive(false);
7144                 prev = v;
7145             }
7146         });
7147
7148         item.setActive(true);
7149         
7150         this.fireEvent('changed', this, item, prev);
7151     },
7152     
7153     getBarItem: function(rid)
7154     {
7155         var ret = false;
7156         
7157         Roo.each(this.barItems, function(e) {
7158             if (e.rid != rid) {
7159                 return;
7160             }
7161             
7162             ret =  e;
7163             return false;
7164         });
7165         
7166         return ret;
7167     },
7168     
7169     indexOfItem : function(item)
7170     {
7171         var index = false;
7172         
7173         Roo.each(this.barItems, function(v, i){
7174             
7175             if (v.rid != item.rid) {
7176                 return;
7177             }
7178             
7179             index = i;
7180             return false
7181         });
7182         
7183         return index;
7184     },
7185     
7186     setActiveNext : function()
7187     {
7188         var i = this.indexOfItem(this.getActive());
7189         
7190         if (i > this.barItems.length) {
7191             return;
7192         }
7193         
7194         this.setActiveItem(this.barItems[i+1]);
7195     },
7196     
7197     setActivePrev : function()
7198     {
7199         var i = this.indexOfItem(this.getActive());
7200         
7201         if (i  < 1) {
7202             return;
7203         }
7204         
7205         this.setActiveItem(this.barItems[i-1]);
7206     },
7207     
7208     format : function()
7209     {
7210         if(!this.barItems.length){
7211             return;
7212         }
7213      
7214         var width = 100 / this.barItems.length;
7215         
7216         Roo.each(this.barItems, function(i){
7217             i.el.setStyle('width', width + '%');
7218             i.topEl.el.setStyle('width', width + '%');
7219             i.bottomEl.el.setStyle('width', width + '%');
7220         }, this);
7221         
7222     }
7223     
7224 });
7225 /*
7226  * - LGPL
7227  *
7228  * Nav Progress Item
7229  * 
7230  */
7231
7232 /**
7233  * @class Roo.bootstrap.nav.ProgressBarItem
7234  * @extends Roo.bootstrap.Component
7235  * Bootstrap NavProgressBarItem class
7236  * @cfg {String} rid the reference id
7237  * @cfg {Boolean} active (true|false) Is item active default false
7238  * @cfg {Boolean} disabled (true|false) Is item active default false
7239  * @cfg {String} html
7240  * @cfg {String} position (top|bottom) text position default bottom
7241  * @cfg {String} icon show icon instead of number
7242  * 
7243  * @constructor
7244  * Create a new NavProgressBarItem
7245  * @param {Object} config The config object
7246  */
7247 Roo.bootstrap.nav.ProgressBarItem = function(config){
7248     Roo.bootstrap.nav.ProgressBarItem.superclass.constructor.call(this, config);
7249     this.addEvents({
7250         // raw events
7251         /**
7252          * @event click
7253          * The raw click event for the entire grid.
7254          * @param {Roo.bootstrap.nav.ProgressBarItem} this
7255          * @param {Roo.EventObject} e
7256          */
7257         "click" : true
7258     });
7259    
7260 };
7261
7262 Roo.extend(Roo.bootstrap.nav.ProgressBarItem, Roo.bootstrap.Component,  {
7263     
7264     rid : '',
7265     active : false,
7266     disabled : false,
7267     html : '',
7268     position : 'bottom',
7269     icon : false,
7270     
7271     getAutoCreate : function()
7272     {
7273         var iconCls = 'roo-navigation-bar-item-icon';
7274         
7275         iconCls += ((this.icon) ? (' ' + this.icon) : (' step-number')) ;
7276         
7277         var cfg = {
7278             tag: 'li',
7279             cls: 'roo-navigation-bar-item',
7280             cn : [
7281                 {
7282                     tag : 'i',
7283                     cls : iconCls
7284                 }
7285             ]
7286         };
7287         
7288         if(this.active){
7289             cfg.cls += ' active';
7290         }
7291         if(this.disabled){
7292             cfg.cls += ' disabled';
7293         }
7294         
7295         return cfg;
7296     },
7297     
7298     disable : function()
7299     {
7300         this.setDisabled(true);
7301     },
7302     
7303     enable : function()
7304     {
7305         this.setDisabled(false);
7306     },
7307     
7308     initEvents: function() 
7309     {
7310         this.iconEl = this.el.select('.roo-navigation-bar-item-icon', true).first();
7311         
7312         this.iconEl.on('click', this.onClick, this);
7313     },
7314     
7315     onClick : function(e)
7316     {
7317         e.preventDefault();
7318         
7319         if(this.disabled){
7320             return;
7321         }
7322         
7323         if(this.fireEvent('click', this, e) === false){
7324             return;
7325         };
7326         
7327         this.parent().setActiveItem(this);
7328     },
7329     
7330     isActive: function () 
7331     {
7332         return this.active;
7333     },
7334     
7335     setActive : function(state)
7336     {
7337         if(this.active == state){
7338             return;
7339         }
7340         
7341         this.active = state;
7342         
7343         if (state) {
7344             this.el.addClass('active');
7345             return;
7346         }
7347         
7348         this.el.removeClass('active');
7349         
7350         return;
7351     },
7352     
7353     setDisabled : function(state)
7354     {
7355         if(this.disabled == state){
7356             return;
7357         }
7358         
7359         this.disabled = state;
7360         
7361         if (state) {
7362             this.el.addClass('disabled');
7363             return;
7364         }
7365         
7366         this.el.removeClass('disabled');
7367     },
7368     
7369     tooltipEl : function()
7370     {
7371         return this.el.select('.roo-navigation-bar-item-icon', true).first();;
7372     }
7373 });
7374  
7375
7376  /*
7377  * - LGPL
7378  *
7379  *  Breadcrumb Nav
7380  * 
7381  */
7382 Roo.namespace('Roo.bootstrap.breadcrumb');
7383
7384
7385 /**
7386  * @class Roo.bootstrap.breadcrumb.Nav
7387  * @extends Roo.bootstrap.Component
7388  * Bootstrap Breadcrumb Nav Class
7389  *  
7390  * @children Roo.bootstrap.breadcrumb.Item
7391  * 
7392  * @constructor
7393  * Create a new breadcrumb.Nav
7394  * @param {Object} config The config object
7395  */
7396
7397
7398 Roo.bootstrap.breadcrumb.Nav = function(config){
7399     Roo.bootstrap.breadcrumb.Nav.superclass.constructor.call(this, config);
7400     
7401     
7402 };
7403
7404 Roo.extend(Roo.bootstrap.breadcrumb.Nav, Roo.bootstrap.Component,  {
7405     
7406     getAutoCreate : function()
7407     {
7408
7409         var cfg = {
7410             tag: 'nav',
7411             cn : [
7412                 {
7413                     tag : 'ol',
7414                     cls : 'breadcrumb'
7415                 }
7416             ]
7417             
7418         };
7419           
7420         return cfg;
7421     },
7422     
7423     initEvents: function()
7424     {
7425         this.olEl = this.el.select('ol',true).first();    
7426     },
7427     getChildContainer : function()
7428     {
7429         return this.olEl;  
7430     }
7431     
7432 });
7433
7434  /*
7435  * - LGPL
7436  *
7437  *  Breadcrumb Item
7438  * 
7439  */
7440
7441
7442 /**
7443  * @class Roo.bootstrap.breadcrumb.Nav
7444  * @extends Roo.bootstrap.Component
7445  * @children Roo.bootstrap.Component
7446  * @parent Roo.bootstrap.breadcrumb.Nav
7447  * Bootstrap Breadcrumb Nav Class
7448  *  
7449  * 
7450  * @cfg {String} html the content of the link.
7451  * @cfg {String} href where it links to if '#' is used the link will be handled by onClick.
7452  * @cfg {Boolean} active is it active
7453
7454  * 
7455  * @constructor
7456  * Create a new breadcrumb.Nav
7457  * @param {Object} config The config object
7458  */
7459
7460 Roo.bootstrap.breadcrumb.Item = function(config){
7461     Roo.bootstrap.breadcrumb.Item.superclass.constructor.call(this, config);
7462     this.addEvents({
7463         // img events
7464         /**
7465          * @event click
7466          * The img click event for the img.
7467          * @param {Roo.EventObject} e
7468          */
7469         "click" : true
7470     });
7471     
7472 };
7473
7474 Roo.extend(Roo.bootstrap.breadcrumb.Item, Roo.bootstrap.Component,  {
7475     
7476     href: false,
7477     html : '',
7478     
7479     getAutoCreate : function()
7480     {
7481
7482         var cfg = {
7483             tag: 'li',
7484             cls : 'breadcrumb-item' + (this.active ? ' active' : '')
7485         };
7486         if (this.href !== false) {
7487             cfg.cn = [{
7488                 tag : 'a',
7489                 href : this.href,
7490                 html : this.html
7491             }];
7492         } else {
7493             cfg.html = this.html;
7494         }
7495         
7496         return cfg;
7497     },
7498     
7499     initEvents: function()
7500     {
7501         if (this.href) {
7502             this.el.select('a', true).first().on('click',this.onClick, this)
7503         }
7504         
7505     },
7506     onClick : function(e)
7507     {
7508         e.preventDefault();
7509         this.fireEvent('click',this,  e);
7510     }
7511     
7512 });
7513
7514  /*
7515  * - LGPL
7516  *
7517  * row
7518  * 
7519  */
7520
7521 /**
7522  * @class Roo.bootstrap.Row
7523  * @extends Roo.bootstrap.Component
7524  * @children Roo.bootstrap.Component
7525  * Bootstrap Row class (contains columns...)
7526  * 
7527  * @constructor
7528  * Create a new Row
7529  * @param {Object} config The config object
7530  */
7531
7532 Roo.bootstrap.Row = function(config){
7533     Roo.bootstrap.Row.superclass.constructor.call(this, config);
7534 };
7535
7536 Roo.extend(Roo.bootstrap.Row, Roo.bootstrap.Component,  {
7537     
7538     getAutoCreate : function(){
7539        return {
7540             cls: 'row clearfix'
7541        };
7542     }
7543     
7544     
7545 });
7546
7547  
7548
7549  /*
7550  * - LGPL
7551  *
7552  * pagination
7553  * 
7554  */
7555
7556 /**
7557  * @class Roo.bootstrap.Pagination
7558  * @extends Roo.bootstrap.Component
7559  * @children Roo.bootstrap.Pagination
7560  * Bootstrap Pagination class
7561  * 
7562  * @cfg {String} size (xs|sm|md|lg|xl)
7563  * @cfg {Boolean} inverse 
7564  * 
7565  * @constructor
7566  * Create a new Pagination
7567  * @param {Object} config The config object
7568  */
7569
7570 Roo.bootstrap.Pagination = function(config){
7571     Roo.bootstrap.Pagination.superclass.constructor.call(this, config);
7572 };
7573
7574 Roo.extend(Roo.bootstrap.Pagination, Roo.bootstrap.Component,  {
7575     
7576     cls: false,
7577     size: false,
7578     inverse: false,
7579     
7580     getAutoCreate : function(){
7581         var cfg = {
7582             tag: 'ul',
7583                 cls: 'pagination'
7584         };
7585         if (this.inverse) {
7586             cfg.cls += ' inverse';
7587         }
7588         if (this.html) {
7589             cfg.html=this.html;
7590         }
7591         if (this.cls) {
7592             cfg.cls += " " + this.cls;
7593         }
7594         return cfg;
7595     }
7596    
7597 });
7598
7599  
7600
7601  /*
7602  * - LGPL
7603  *
7604  * Pagination item
7605  * 
7606  */
7607
7608
7609 /**
7610  * @class Roo.bootstrap.PaginationItem
7611  * @extends Roo.bootstrap.Component
7612  * Bootstrap PaginationItem class
7613  * @cfg {String} html text
7614  * @cfg {String} href the link
7615  * @cfg {Boolean} preventDefault (true | false) default true
7616  * @cfg {Boolean} active (true | false) default false
7617  * @cfg {Boolean} disabled default false
7618  * 
7619  * 
7620  * @constructor
7621  * Create a new PaginationItem
7622  * @param {Object} config The config object
7623  */
7624
7625
7626 Roo.bootstrap.PaginationItem = function(config){
7627     Roo.bootstrap.PaginationItem.superclass.constructor.call(this, config);
7628     this.addEvents({
7629         // raw events
7630         /**
7631          * @event click
7632          * The raw click event for the entire grid.
7633          * @param {Roo.EventObject} e
7634          */
7635         "click" : true
7636     });
7637 };
7638
7639 Roo.extend(Roo.bootstrap.PaginationItem, Roo.bootstrap.Component,  {
7640     
7641     href : false,
7642     html : false,
7643     preventDefault: true,
7644     active : false,
7645     cls : false,
7646     disabled: false,
7647     
7648     getAutoCreate : function(){
7649         var cfg= {
7650             tag: 'li',
7651             cn: [
7652                 {
7653                     tag : 'a',
7654                     href : this.href ? this.href : '#',
7655                     html : this.html ? this.html : ''
7656                 }
7657             ]
7658         };
7659         
7660         if(this.cls){
7661             cfg.cls = this.cls;
7662         }
7663         
7664         if(this.disabled){
7665             cfg.cls = typeof(cfg.cls) !== 'undefined' ? cfg.cls + ' disabled' : 'disabled';
7666         }
7667         
7668         if(this.active){
7669             cfg.cls = typeof(cfg.cls) !== 'undefined' ? cfg.cls + ' active' : 'active';
7670         }
7671         
7672         return cfg;
7673     },
7674     
7675     initEvents: function() {
7676         
7677         this.el.on('click', this.onClick, this);
7678         
7679     },
7680     onClick : function(e)
7681     {
7682         Roo.log('PaginationItem on click ');
7683         if(this.preventDefault){
7684             e.preventDefault();
7685         }
7686         
7687         if(this.disabled){
7688             return;
7689         }
7690         
7691         this.fireEvent('click', this, e);
7692     }
7693    
7694 });
7695
7696  
7697
7698  /*
7699  * - LGPL
7700  *
7701  * slider
7702  * 
7703  */
7704
7705
7706 /**
7707  * @class Roo.bootstrap.Slider
7708  * @extends Roo.bootstrap.Component
7709  * Bootstrap Slider class
7710  *    
7711  * @constructor
7712  * Create a new Slider
7713  * @param {Object} config The config object
7714  */
7715
7716 Roo.bootstrap.Slider = function(config){
7717     Roo.bootstrap.Slider.superclass.constructor.call(this, config);
7718 };
7719
7720 Roo.extend(Roo.bootstrap.Slider, Roo.bootstrap.Component,  {
7721     
7722     getAutoCreate : function(){
7723         
7724         var cfg = {
7725             tag: 'div',
7726             cls: 'slider slider-sample1 vertical-handler ui-slider ui-slider-horizontal ui-widget ui-widget-content ui-corner-all',
7727             cn: [
7728                 {
7729                     tag: 'a',
7730                     cls: 'ui-slider-handle ui-state-default ui-corner-all'
7731                 }
7732             ]
7733         };
7734         
7735         return cfg;
7736     }
7737    
7738 });
7739
7740  /*
7741  * Based on:
7742  * Ext JS Library 1.1.1
7743  * Copyright(c) 2006-2007, Ext JS, LLC.
7744  *
7745  * Originally Released Under LGPL - original licence link has changed is not relivant.
7746  *
7747  * Fork - LGPL
7748  * <script type="text/javascript">
7749  */
7750  /**
7751  * @extends Roo.dd.DDProxy
7752  * @class Roo.grid.SplitDragZone
7753  * Support for Column Header resizing
7754  * @constructor
7755  * @param {Object} config
7756  */
7757 // private
7758 // This is a support class used internally by the Grid components
7759 Roo.grid.SplitDragZone = function(grid, hd, hd2){
7760     this.grid = grid;
7761     this.view = grid.getView();
7762     this.proxy = this.view.resizeProxy;
7763     Roo.grid.SplitDragZone.superclass.constructor.call(
7764         this,
7765         hd, // ID
7766         "gridSplitters" + this.grid.getGridEl().id, // SGROUP
7767         {  // CONFIG
7768             dragElId : Roo.id(this.proxy.dom),
7769             resizeFrame:false
7770         }
7771     );
7772     
7773     this.setHandleElId(Roo.id(hd));
7774     if (hd2 !== false) {
7775         this.setOuterHandleElId(Roo.id(hd2));
7776     }
7777     
7778     this.scroll = false;
7779 };
7780 Roo.extend(Roo.grid.SplitDragZone, Roo.dd.DDProxy, {
7781     fly: Roo.Element.fly,
7782
7783     b4StartDrag : function(x, y){
7784         this.view.headersDisabled = true;
7785         var h = this.view.mainWrap ? this.view.mainWrap.getHeight() : (
7786                     this.view.headEl.getHeight() + this.view.bodyEl.getHeight()
7787         );
7788         this.proxy.setHeight(h);
7789         
7790         // for old system colWidth really stored the actual width?
7791         // in bootstrap we tried using xs/ms/etc.. to do % sizing?
7792         // which in reality did not work.. - it worked only for fixed sizes
7793         // for resizable we need to use actual sizes.
7794         var w = this.cm.getColumnWidth(this.cellIndex);
7795         if (!this.view.mainWrap) {
7796             // bootstrap.
7797             w = this.view.getHeaderIndex(this.cellIndex).getWidth();
7798         }
7799         
7800         
7801         
7802         // this was w-this.grid.minColumnWidth;
7803         // doesnt really make sense? - w = thie curren width or the rendered one?
7804         var minw = Math.max(w-this.grid.minColumnWidth, 0);
7805         this.resetConstraints();
7806         this.setXConstraint(minw, 1000);
7807         this.setYConstraint(0, 0);
7808         this.minX = x - minw;
7809         this.maxX = x + 1000;
7810         this.startPos = x;
7811         if (!this.view.mainWrap) { // this is Bootstrap code..
7812             this.getDragEl().style.display='block';
7813         }
7814         
7815         Roo.dd.DDProxy.prototype.b4StartDrag.call(this, x, y);
7816     },
7817
7818
7819     handleMouseDown : function(e){
7820         ev = Roo.EventObject.setEvent(e);
7821         var t = this.fly(ev.getTarget());
7822         if(t.hasClass("x-grid-split")){
7823             this.cellIndex = this.view.getCellIndex(t.dom);
7824             this.split = t.dom;
7825             this.cm = this.grid.colModel;
7826             if(this.cm.isResizable(this.cellIndex) && !this.cm.isFixed(this.cellIndex)){
7827                 Roo.grid.SplitDragZone.superclass.handleMouseDown.apply(this, arguments);
7828             }
7829         }
7830     },
7831
7832     endDrag : function(e){
7833         this.view.headersDisabled = false;
7834         var endX = Math.max(this.minX, Roo.lib.Event.getPageX(e));
7835         var diff = endX - this.startPos;
7836         // 
7837         var w = this.cm.getColumnWidth(this.cellIndex);
7838         if (!this.view.mainWrap) {
7839             w = 0;
7840         }
7841         this.view.onColumnSplitterMoved(this.cellIndex, w+diff);
7842     },
7843
7844     autoOffset : function(){
7845         this.setDelta(0,0);
7846     }
7847 });/*
7848  * Based on:
7849  * Ext JS Library 1.1.1
7850  * Copyright(c) 2006-2007, Ext JS, LLC.
7851  *
7852  * Originally Released Under LGPL - original licence link has changed is not relivant.
7853  *
7854  * Fork - LGPL
7855  * <script type="text/javascript">
7856  */
7857
7858 /**
7859  * @class Roo.grid.AbstractSelectionModel
7860  * @extends Roo.util.Observable
7861  * @abstract
7862  * Abstract base class for grid SelectionModels.  It provides the interface that should be
7863  * implemented by descendant classes.  This class should not be directly instantiated.
7864  * @constructor
7865  */
7866 Roo.grid.AbstractSelectionModel = function(){
7867     this.locked = false;
7868     Roo.grid.AbstractSelectionModel.superclass.constructor.call(this);
7869 };
7870
7871 Roo.extend(Roo.grid.AbstractSelectionModel, Roo.util.Observable,  {
7872     /** @ignore Called by the grid automatically. Do not call directly. */
7873     init : function(grid){
7874         this.grid = grid;
7875         this.initEvents();
7876     },
7877
7878     /**
7879      * Locks the selections.
7880      */
7881     lock : function(){
7882         this.locked = true;
7883     },
7884
7885     /**
7886      * Unlocks the selections.
7887      */
7888     unlock : function(){
7889         this.locked = false;
7890     },
7891
7892     /**
7893      * Returns true if the selections are locked.
7894      * @return {Boolean}
7895      */
7896     isLocked : function(){
7897         return this.locked;
7898     }
7899 });/*
7900  * Based on:
7901  * Ext JS Library 1.1.1
7902  * Copyright(c) 2006-2007, Ext JS, LLC.
7903  *
7904  * Originally Released Under LGPL - original licence link has changed is not relivant.
7905  *
7906  * Fork - LGPL
7907  * <script type="text/javascript">
7908  */
7909 /**
7910  * @extends Roo.grid.AbstractSelectionModel
7911  * @class Roo.grid.RowSelectionModel
7912  * The default SelectionModel used by {@link Roo.grid.Grid}.
7913  * It supports multiple selections and keyboard selection/navigation. 
7914  * @constructor
7915  * @param {Object} config
7916  */
7917 Roo.grid.RowSelectionModel = function(config){
7918     Roo.apply(this, config);
7919     this.selections = new Roo.util.MixedCollection(false, function(o){
7920         return o.id;
7921     });
7922
7923     this.last = false;
7924     this.lastActive = false;
7925
7926     this.addEvents({
7927         /**
7928         * @event selectionchange
7929         * Fires when the selection changes
7930         * @param {SelectionModel} this
7931         */
7932        "selectionchange" : true,
7933        /**
7934         * @event afterselectionchange
7935         * Fires after the selection changes (eg. by key press or clicking)
7936         * @param {SelectionModel} this
7937         */
7938        "afterselectionchange" : true,
7939        /**
7940         * @event beforerowselect
7941         * Fires when a row is selected being selected, return false to cancel.
7942         * @param {SelectionModel} this
7943         * @param {Number} rowIndex The selected index
7944         * @param {Boolean} keepExisting False if other selections will be cleared
7945         */
7946        "beforerowselect" : true,
7947        /**
7948         * @event rowselect
7949         * Fires when a row is selected.
7950         * @param {SelectionModel} this
7951         * @param {Number} rowIndex The selected index
7952         * @param {Roo.data.Record} r The record
7953         */
7954        "rowselect" : true,
7955        /**
7956         * @event rowdeselect
7957         * Fires when a row is deselected.
7958         * @param {SelectionModel} this
7959         * @param {Number} rowIndex The selected index
7960         */
7961         "rowdeselect" : true
7962     });
7963     Roo.grid.RowSelectionModel.superclass.constructor.call(this);
7964     this.locked = false;
7965 };
7966
7967 Roo.extend(Roo.grid.RowSelectionModel, Roo.grid.AbstractSelectionModel,  {
7968     /**
7969      * @cfg {Boolean} singleSelect
7970      * True to allow selection of only one row at a time (defaults to false)
7971      */
7972     singleSelect : false,
7973
7974     // private
7975     initEvents : function(){
7976
7977         if(!this.grid.enableDragDrop && !this.grid.enableDrag){
7978             this.grid.on("mousedown", this.handleMouseDown, this);
7979         }else{ // allow click to work like normal
7980             this.grid.on("rowclick", this.handleDragableRowClick, this);
7981         }
7982         // bootstrap does not have a view..
7983         var view = this.grid.view ? this.grid.view : this.grid;
7984         this.rowNav = new Roo.KeyNav(this.grid.getGridEl(), {
7985             "up" : function(e){
7986                 if(!e.shiftKey){
7987                     this.selectPrevious(e.shiftKey);
7988                 }else if(this.last !== false && this.lastActive !== false){
7989                     var last = this.last;
7990                     this.selectRange(this.last,  this.lastActive-1);
7991                     view.focusRow(this.lastActive);
7992                     if(last !== false){
7993                         this.last = last;
7994                     }
7995                 }else{
7996                     this.selectFirstRow();
7997                 }
7998                 this.fireEvent("afterselectionchange", this);
7999             },
8000             "down" : function(e){
8001                 if(!e.shiftKey){
8002                     this.selectNext(e.shiftKey);
8003                 }else if(this.last !== false && this.lastActive !== false){
8004                     var last = this.last;
8005                     this.selectRange(this.last,  this.lastActive+1);
8006                     view.focusRow(this.lastActive);
8007                     if(last !== false){
8008                         this.last = last;
8009                     }
8010                 }else{
8011                     this.selectFirstRow();
8012                 }
8013                 this.fireEvent("afterselectionchange", this);
8014             },
8015             scope: this
8016         });
8017
8018          
8019         view.on("refresh", this.onRefresh, this);
8020         view.on("rowupdated", this.onRowUpdated, this);
8021         view.on("rowremoved", this.onRemove, this);
8022     },
8023
8024     // private
8025     onRefresh : function(){
8026         var ds = this.grid.ds, i, v = this.grid.view;
8027         var s = this.selections;
8028         s.each(function(r){
8029             if((i = ds.indexOfId(r.id)) != -1){
8030                 v.onRowSelect(i);
8031                 s.add(ds.getAt(i)); // updating the selection relate data
8032             }else{
8033                 s.remove(r);
8034             }
8035         });
8036     },
8037
8038     // private
8039     onRemove : function(v, index, r){
8040         this.selections.remove(r);
8041     },
8042
8043     // private
8044     onRowUpdated : function(v, index, r){
8045         if(this.isSelected(r)){
8046             v.onRowSelect(index);
8047         }
8048     },
8049
8050     /**
8051      * Select records.
8052      * @param {Array} records The records to select
8053      * @param {Boolean} keepExisting (optional) True to keep existing selections
8054      */
8055     selectRecords : function(records, keepExisting){
8056         if(!keepExisting){
8057             this.clearSelections();
8058         }
8059         var ds = this.grid.ds;
8060         for(var i = 0, len = records.length; i < len; i++){
8061             this.selectRow(ds.indexOf(records[i]), true);
8062         }
8063     },
8064
8065     /**
8066      * Gets the number of selected rows.
8067      * @return {Number}
8068      */
8069     getCount : function(){
8070         return this.selections.length;
8071     },
8072
8073     /**
8074      * Selects the first row in the grid.
8075      */
8076     selectFirstRow : function(){
8077         this.selectRow(0);
8078     },
8079
8080     /**
8081      * Select the last row.
8082      * @param {Boolean} keepExisting (optional) True to keep existing selections
8083      */
8084     selectLastRow : function(keepExisting){
8085         this.selectRow(this.grid.ds.getCount() - 1, keepExisting);
8086     },
8087
8088     /**
8089      * Selects the row immediately following the last selected row.
8090      * @param {Boolean} keepExisting (optional) True to keep existing selections
8091      */
8092     selectNext : function(keepExisting){
8093         if(this.last !== false && (this.last+1) < this.grid.ds.getCount()){
8094             this.selectRow(this.last+1, keepExisting);
8095             var view = this.grid.view ? this.grid.view : this.grid;
8096             view.focusRow(this.last);
8097         }
8098     },
8099
8100     /**
8101      * Selects the row that precedes the last selected row.
8102      * @param {Boolean} keepExisting (optional) True to keep existing selections
8103      */
8104     selectPrevious : function(keepExisting){
8105         if(this.last){
8106             this.selectRow(this.last-1, keepExisting);
8107             var view = this.grid.view ? this.grid.view : this.grid;
8108             view.focusRow(this.last);
8109         }
8110     },
8111
8112     /**
8113      * Returns the selected records
8114      * @return {Array} Array of selected records
8115      */
8116     getSelections : function(){
8117         return [].concat(this.selections.items);
8118     },
8119
8120     /**
8121      * Returns the first selected record.
8122      * @return {Record}
8123      */
8124     getSelected : function(){
8125         return this.selections.itemAt(0);
8126     },
8127
8128
8129     /**
8130      * Clears all selections.
8131      */
8132     clearSelections : function(fast){
8133         if(this.locked) {
8134             return;
8135         }
8136         if(fast !== true){
8137             var ds = this.grid.ds;
8138             var s = this.selections;
8139             s.each(function(r){
8140                 this.deselectRow(ds.indexOfId(r.id));
8141             }, this);
8142             s.clear();
8143         }else{
8144             this.selections.clear();
8145         }
8146         this.last = false;
8147     },
8148
8149
8150     /**
8151      * Selects all rows.
8152      */
8153     selectAll : function(){
8154         if(this.locked) {
8155             return;
8156         }
8157         this.selections.clear();
8158         for(var i = 0, len = this.grid.ds.getCount(); i < len; i++){
8159             this.selectRow(i, true);
8160         }
8161     },
8162
8163     /**
8164      * Returns True if there is a selection.
8165      * @return {Boolean}
8166      */
8167     hasSelection : function(){
8168         return this.selections.length > 0;
8169     },
8170
8171     /**
8172      * Returns True if the specified row is selected.
8173      * @param {Number/Record} record The record or index of the record to check
8174      * @return {Boolean}
8175      */
8176     isSelected : function(index){
8177         var r = typeof index == "number" ? this.grid.ds.getAt(index) : index;
8178         return (r && this.selections.key(r.id) ? true : false);
8179     },
8180
8181     /**
8182      * Returns True if the specified record id is selected.
8183      * @param {String} id The id of record to check
8184      * @return {Boolean}
8185      */
8186     isIdSelected : function(id){
8187         return (this.selections.key(id) ? true : false);
8188     },
8189
8190     // private
8191     handleMouseDown : function(e, t)
8192     {
8193         var view = this.grid.view ? this.grid.view : this.grid;
8194         var rowIndex;
8195         if(this.isLocked() || (rowIndex = view.findRowIndex(t)) === false){
8196             return;
8197         };
8198         if(e.shiftKey && this.last !== false){
8199             var last = this.last;
8200             this.selectRange(last, rowIndex, e.ctrlKey);
8201             this.last = last; // reset the last
8202             view.focusRow(rowIndex);
8203         }else{
8204             var isSelected = this.isSelected(rowIndex);
8205             if(e.button !== 0 && isSelected){
8206                 view.focusRow(rowIndex);
8207             }else if(e.ctrlKey && isSelected){
8208                 this.deselectRow(rowIndex);
8209             }else if(!isSelected){
8210                 this.selectRow(rowIndex, e.button === 0 && (e.ctrlKey || e.shiftKey));
8211                 view.focusRow(rowIndex);
8212             }
8213         }
8214         this.fireEvent("afterselectionchange", this);
8215     },
8216     // private
8217     handleDragableRowClick :  function(grid, rowIndex, e) 
8218     {
8219         if(e.button === 0 && !e.shiftKey && !e.ctrlKey) {
8220             this.selectRow(rowIndex, false);
8221             var view = this.grid.view ? this.grid.view : this.grid;
8222             view.focusRow(rowIndex);
8223              this.fireEvent("afterselectionchange", this);
8224         }
8225     },
8226     
8227     /**
8228      * Selects multiple rows.
8229      * @param {Array} rows Array of the indexes of the row to select
8230      * @param {Boolean} keepExisting (optional) True to keep existing selections
8231      */
8232     selectRows : function(rows, keepExisting){
8233         if(!keepExisting){
8234             this.clearSelections();
8235         }
8236         for(var i = 0, len = rows.length; i < len; i++){
8237             this.selectRow(rows[i], true);
8238         }
8239     },
8240
8241     /**
8242      * Selects a range of rows. All rows in between startRow and endRow are also selected.
8243      * @param {Number} startRow The index of the first row in the range
8244      * @param {Number} endRow The index of the last row in the range
8245      * @param {Boolean} keepExisting (optional) True to retain existing selections
8246      */
8247     selectRange : function(startRow, endRow, keepExisting){
8248         if(this.locked) {
8249             return;
8250         }
8251         if(!keepExisting){
8252             this.clearSelections();
8253         }
8254         if(startRow <= endRow){
8255             for(var i = startRow; i <= endRow; i++){
8256                 this.selectRow(i, true);
8257             }
8258         }else{
8259             for(var i = startRow; i >= endRow; i--){
8260                 this.selectRow(i, true);
8261             }
8262         }
8263     },
8264
8265     /**
8266      * Deselects a range of rows. All rows in between startRow and endRow are also deselected.
8267      * @param {Number} startRow The index of the first row in the range
8268      * @param {Number} endRow The index of the last row in the range
8269      */
8270     deselectRange : function(startRow, endRow, preventViewNotify){
8271         if(this.locked) {
8272             return;
8273         }
8274         for(var i = startRow; i <= endRow; i++){
8275             this.deselectRow(i, preventViewNotify);
8276         }
8277     },
8278
8279     /**
8280      * Selects a row.
8281      * @param {Number} row The index of the row to select
8282      * @param {Boolean} keepExisting (optional) True to keep existing selections
8283      */
8284     selectRow : function(index, keepExisting, preventViewNotify){
8285         if(this.locked || (index < 0 || index >= this.grid.ds.getCount())) {
8286             return;
8287         }
8288         if(this.fireEvent("beforerowselect", this, index, keepExisting) !== false){
8289             if(!keepExisting || this.singleSelect){
8290                 this.clearSelections();
8291             }
8292             var r = this.grid.ds.getAt(index);
8293             this.selections.add(r);
8294             this.last = this.lastActive = index;
8295             if(!preventViewNotify){
8296                 var view = this.grid.view ? this.grid.view : this.grid;
8297                 view.onRowSelect(index);
8298             }
8299             this.fireEvent("rowselect", this, index, r);
8300             this.fireEvent("selectionchange", this);
8301         }
8302     },
8303
8304     /**
8305      * Deselects a row.
8306      * @param {Number} row The index of the row to deselect
8307      */
8308     deselectRow : function(index, preventViewNotify){
8309         if(this.locked) {
8310             return;
8311         }
8312         if(this.last == index){
8313             this.last = false;
8314         }
8315         if(this.lastActive == index){
8316             this.lastActive = false;
8317         }
8318         var r = this.grid.ds.getAt(index);
8319         this.selections.remove(r);
8320         if(!preventViewNotify){
8321             var view = this.grid.view ? this.grid.view : this.grid;
8322             view.onRowDeselect(index);
8323         }
8324         this.fireEvent("rowdeselect", this, index);
8325         this.fireEvent("selectionchange", this);
8326     },
8327
8328     // private
8329     restoreLast : function(){
8330         if(this._last){
8331             this.last = this._last;
8332         }
8333     },
8334
8335     // private
8336     acceptsNav : function(row, col, cm){
8337         return !cm.isHidden(col) && cm.isCellEditable(col, row);
8338     },
8339
8340     // private
8341     onEditorKey : function(field, e){
8342         var k = e.getKey(), newCell, g = this.grid, ed = g.activeEditor;
8343         if(k == e.TAB){
8344             e.stopEvent();
8345             ed.completeEdit();
8346             if(e.shiftKey){
8347                 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
8348             }else{
8349                 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
8350             }
8351         }else if(k == e.ENTER && !e.ctrlKey){
8352             e.stopEvent();
8353             ed.completeEdit();
8354             if(e.shiftKey){
8355                 newCell = g.walkCells(ed.row-1, ed.col, -1, this.acceptsNav, this);
8356             }else{
8357                 newCell = g.walkCells(ed.row+1, ed.col, 1, this.acceptsNav, this);
8358             }
8359         }else if(k == e.ESC){
8360             ed.cancelEdit();
8361         }
8362         if(newCell){
8363             g.startEditing(newCell[0], newCell[1]);
8364         }
8365     }
8366 });/*
8367  * Based on:
8368  * Ext JS Library 1.1.1
8369  * Copyright(c) 2006-2007, Ext JS, LLC.
8370  *
8371  * Originally Released Under LGPL - original licence link has changed is not relivant.
8372  *
8373  * Fork - LGPL
8374  * <script type="text/javascript">
8375  */
8376  
8377
8378 /**
8379  * @class Roo.grid.ColumnModel
8380  * @extends Roo.util.Observable
8381  * This is the default implementation of a ColumnModel used by the Grid. It defines
8382  * the columns in the grid.
8383  * <br>Usage:<br>
8384  <pre><code>
8385  var colModel = new Roo.grid.ColumnModel([
8386         {header: "Ticker", width: 60, sortable: true, locked: true},
8387         {header: "Company Name", width: 150, sortable: true},
8388         {header: "Market Cap.", width: 100, sortable: true},
8389         {header: "$ Sales", width: 100, sortable: true, renderer: money},
8390         {header: "Employees", width: 100, sortable: true, resizable: false}
8391  ]);
8392  </code></pre>
8393  * <p>
8394  
8395  * The config options listed for this class are options which may appear in each
8396  * individual column definition.
8397  * <br/>RooJS Fix - column id's are not sequential but use Roo.id() - fixes bugs with layouts.
8398  * @constructor
8399  * @param {Object} config An Array of column config objects. See this class's
8400  * config objects for details.
8401 */
8402 Roo.grid.ColumnModel = function(config){
8403         /**
8404      * The config passed into the constructor
8405      */
8406     this.config = []; //config;
8407     this.lookup = {};
8408
8409     // if no id, create one
8410     // if the column does not have a dataIndex mapping,
8411     // map it to the order it is in the config
8412     for(var i = 0, len = config.length; i < len; i++){
8413         this.addColumn(config[i]);
8414         
8415     }
8416
8417     /**
8418      * The width of columns which have no width specified (defaults to 100)
8419      * @type Number
8420      */
8421     this.defaultWidth = 100;
8422
8423     /**
8424      * Default sortable of columns which have no sortable specified (defaults to false)
8425      * @type Boolean
8426      */
8427     this.defaultSortable = false;
8428
8429     this.addEvents({
8430         /**
8431              * @event widthchange
8432              * Fires when the width of a column changes.
8433              * @param {ColumnModel} this
8434              * @param {Number} columnIndex The column index
8435              * @param {Number} newWidth The new width
8436              */
8437             "widthchange": true,
8438         /**
8439              * @event headerchange
8440              * Fires when the text of a header changes.
8441              * @param {ColumnModel} this
8442              * @param {Number} columnIndex The column index
8443              * @param {Number} newText The new header text
8444              */
8445             "headerchange": true,
8446         /**
8447              * @event hiddenchange
8448              * Fires when a column is hidden or "unhidden".
8449              * @param {ColumnModel} this
8450              * @param {Number} columnIndex The column index
8451              * @param {Boolean} hidden true if hidden, false otherwise
8452              */
8453             "hiddenchange": true,
8454             /**
8455          * @event columnmoved
8456          * Fires when a column is moved.
8457          * @param {ColumnModel} this
8458          * @param {Number} oldIndex
8459          * @param {Number} newIndex
8460          */
8461         "columnmoved" : true,
8462         /**
8463          * @event columlockchange
8464          * Fires when a column's locked state is changed
8465          * @param {ColumnModel} this
8466          * @param {Number} colIndex
8467          * @param {Boolean} locked true if locked
8468          */
8469         "columnlockchange" : true
8470     });
8471     Roo.grid.ColumnModel.superclass.constructor.call(this);
8472 };
8473 Roo.extend(Roo.grid.ColumnModel, Roo.util.Observable, {
8474     /**
8475      * @cfg {String} header [required] The header text to display in the Grid view.
8476      */
8477         /**
8478      * @cfg {String} xsHeader Header at Bootsrap Extra Small width (default for all)
8479      */
8480         /**
8481      * @cfg {String} smHeader Header at Bootsrap Small width
8482      */
8483         /**
8484      * @cfg {String} mdHeader Header at Bootsrap Medium width
8485      */
8486         /**
8487      * @cfg {String} lgHeader Header at Bootsrap Large width
8488      */
8489         /**
8490      * @cfg {String} xlHeader Header at Bootsrap extra Large width
8491      */
8492     /**
8493      * @cfg {String} dataIndex  The name of the field in the grid's {@link Roo.data.Store}'s
8494      * {@link Roo.data.Record} definition from which to draw the column's value. If not
8495      * specified, the column's index is used as an index into the Record's data Array.
8496      */
8497     /**
8498      * @cfg {Number} width  The initial width in pixels of the column. Using this
8499      * instead of {@link Roo.grid.Grid#autoSizeColumns} is more efficient.
8500      */
8501     /**
8502      * @cfg {Boolean} sortable True if sorting is to be allowed on this column.
8503      * Defaults to the value of the {@link #defaultSortable} property.
8504      * Whether local/remote sorting is used is specified in {@link Roo.data.Store#remoteSort}.
8505      */
8506     /**
8507      * @cfg {Boolean} locked  True to lock the column in place while scrolling the Grid.  Defaults to false.
8508      */
8509     /**
8510      * @cfg {Boolean} fixed  True if the column width cannot be changed.  Defaults to false.
8511      */
8512     /**
8513      * @cfg {Boolean} resizable  False to disable column resizing. Defaults to true.
8514      */
8515     /**
8516      * @cfg {Boolean} hidden  True to hide the column. Defaults to false.
8517      */
8518     /**
8519      * @cfg {Function} renderer A function used to generate HTML markup for a cell
8520      * given the cell's data value. See {@link #setRenderer}. If not specified, the
8521      * default renderer returns the escaped data value. If an object is returned (bootstrap only)
8522      * then it is treated as a Roo Component object instance, and it is rendered after the initial row is rendered
8523      */
8524        /**
8525      * @cfg {Roo.grid.GridEditor} editor  For grid editors - returns the grid editor 
8526      */
8527     /**
8528      * @cfg {String} align (left|right) Set the CSS text-align property of the column.  Defaults to undefined (left).
8529      */
8530     /**
8531      * @cfg {String} valign (top|bottom|middle) Set the CSS vertical-align property of the column (eg. middle, top, bottom etc).  Defaults to undefined (middle)
8532      */
8533     /**
8534      * @cfg {String} cursor ( auto|default|none|context-menu|help|pointer|progress|wait|cell|crosshair|text|vertical-text|alias|copy|move|no-drop|not-allowed|e-resize|n-resize|ne-resize|nw-resize|s-resize|se-resize|sw-resize|w-resize|ew-resize|ns-resize|nesw-resize|nwse-resize|col-resize|row-resize|all-scroll|zoom-in|zoom-out|grab|grabbing)
8535      */
8536     /**
8537      * @cfg {String} tooltip mouse over tooltip text
8538      */
8539     /**
8540      * @cfg {Number} xs  can be '0' for hidden at this size (number less than 12)
8541      */
8542     /**
8543      * @cfg {Number} sm can be '0' for hidden at this size (number less than 12)
8544      */
8545     /**
8546      * @cfg {Number} md can be '0' for hidden at this size (number less than 12)
8547      */
8548     /**
8549      * @cfg {Number} lg   can be '0' for hidden at this size (number less than 12)
8550      */
8551         /**
8552      * @cfg {Number} xl   can be '0' for hidden at this size (number less than 12)
8553      */
8554     /**
8555      * Returns the id of the column at the specified index.
8556      * @param {Number} index The column index
8557      * @return {String} the id
8558      */
8559     getColumnId : function(index){
8560         return this.config[index].id;
8561     },
8562
8563     /**
8564      * Returns the column for a specified id.
8565      * @param {String} id The column id
8566      * @return {Object} the column
8567      */
8568     getColumnById : function(id){
8569         return this.lookup[id];
8570     },
8571
8572     
8573     /**
8574      * Returns the column Object for a specified dataIndex.
8575      * @param {String} dataIndex The column dataIndex
8576      * @return {Object|Boolean} the column or false if not found
8577      */
8578     getColumnByDataIndex: function(dataIndex){
8579         var index = this.findColumnIndex(dataIndex);
8580         return index > -1 ? this.config[index] : false;
8581     },
8582     
8583     /**
8584      * Returns the index for a specified column id.
8585      * @param {String} id The column id
8586      * @return {Number} the index, or -1 if not found
8587      */
8588     getIndexById : function(id){
8589         for(var i = 0, len = this.config.length; i < len; i++){
8590             if(this.config[i].id == id){
8591                 return i;
8592             }
8593         }
8594         return -1;
8595     },
8596     
8597     /**
8598      * Returns the index for a specified column dataIndex.
8599      * @param {String} dataIndex The column dataIndex
8600      * @return {Number} the index, or -1 if not found
8601      */
8602     
8603     findColumnIndex : function(dataIndex){
8604         for(var i = 0, len = this.config.length; i < len; i++){
8605             if(this.config[i].dataIndex == dataIndex){
8606                 return i;
8607             }
8608         }
8609         return -1;
8610     },
8611     
8612     
8613     moveColumn : function(oldIndex, newIndex){
8614         var c = this.config[oldIndex];
8615         this.config.splice(oldIndex, 1);
8616         this.config.splice(newIndex, 0, c);
8617         this.dataMap = null;
8618         this.fireEvent("columnmoved", this, oldIndex, newIndex);
8619     },
8620
8621     isLocked : function(colIndex){
8622         return this.config[colIndex].locked === true;
8623     },
8624
8625     setLocked : function(colIndex, value, suppressEvent){
8626         if(this.isLocked(colIndex) == value){
8627             return;
8628         }
8629         this.config[colIndex].locked = value;
8630         if(!suppressEvent){
8631             this.fireEvent("columnlockchange", this, colIndex, value);
8632         }
8633     },
8634
8635     getTotalLockedWidth : function(){
8636         var totalWidth = 0;
8637         for(var i = 0; i < this.config.length; i++){
8638             if(this.isLocked(i) && !this.isHidden(i)){
8639                 this.totalWidth += this.getColumnWidth(i);
8640             }
8641         }
8642         return totalWidth;
8643     },
8644
8645     getLockedCount : function(){
8646         for(var i = 0, len = this.config.length; i < len; i++){
8647             if(!this.isLocked(i)){
8648                 return i;
8649             }
8650         }
8651         
8652         return this.config.length;
8653     },
8654
8655     /**
8656      * Returns the number of columns.
8657      * @return {Number}
8658      */
8659     getColumnCount : function(visibleOnly){
8660         if(visibleOnly === true){
8661             var c = 0;
8662             for(var i = 0, len = this.config.length; i < len; i++){
8663                 if(!this.isHidden(i)){
8664                     c++;
8665                 }
8666             }
8667             return c;
8668         }
8669         return this.config.length;
8670     },
8671
8672     /**
8673      * Returns the column configs that return true by the passed function that is called with (columnConfig, index)
8674      * @param {Function} fn
8675      * @param {Object} scope (optional)
8676      * @return {Array} result
8677      */
8678     getColumnsBy : function(fn, scope){
8679         var r = [];
8680         for(var i = 0, len = this.config.length; i < len; i++){
8681             var c = this.config[i];
8682             if(fn.call(scope||this, c, i) === true){
8683                 r[r.length] = c;
8684             }
8685         }
8686         return r;
8687     },
8688
8689     /**
8690      * Returns true if the specified column is sortable.
8691      * @param {Number} col The column index
8692      * @return {Boolean}
8693      */
8694     isSortable : function(col){
8695         if(typeof this.config[col].sortable == "undefined"){
8696             return this.defaultSortable;
8697         }
8698         return this.config[col].sortable;
8699     },
8700
8701     /**
8702      * Returns the rendering (formatting) function defined for the column.
8703      * @param {Number} col The column index.
8704      * @return {Function} The function used to render the cell. See {@link #setRenderer}.
8705      */
8706     getRenderer : function(col){
8707         if(!this.config[col].renderer){
8708             return Roo.grid.ColumnModel.defaultRenderer;
8709         }
8710         return this.config[col].renderer;
8711     },
8712
8713     /**
8714      * Sets the rendering (formatting) function for a column.
8715      * @param {Number} col The column index
8716      * @param {Function} fn The function to use to process the cell's raw data
8717      * to return HTML markup for the grid view. The render function is called with
8718      * the following parameters:<ul>
8719      * <li>Data value.</li>
8720      * <li>Cell metadata. An object in which you may set the following attributes:<ul>
8721      * <li>css A CSS style string to apply to the table cell.</li>
8722      * <li>attr An HTML attribute definition string to apply to the data container element <i>within</i> the table cell.</li></ul>
8723      * <li>The {@link Roo.data.Record} from which the data was extracted.</li>
8724      * <li>Row index</li>
8725      * <li>Column index</li>
8726      * <li>The {@link Roo.data.Store} object from which the Record was extracted</li></ul>
8727      */
8728     setRenderer : function(col, fn){
8729         this.config[col].renderer = fn;
8730     },
8731
8732     /**
8733      * Returns the width for the specified column.
8734      * @param {Number} col The column index
8735      * @param (optional) {String} gridSize bootstrap width size.
8736      * @return {Number}
8737      */
8738     getColumnWidth : function(col, gridSize)
8739         {
8740                 var cfg = this.config[col];
8741                 
8742                 if (typeof(gridSize) == 'undefined') {
8743                         return cfg.width * 1 || this.defaultWidth;
8744                 }
8745                 if (gridSize === false) { // if we set it..
8746                         return cfg.width || false;
8747                 }
8748                 var sizes = ['xl', 'lg', 'md', 'sm', 'xs'];
8749                 
8750                 for(var i = sizes.indexOf(gridSize); i < sizes.length; i++) {
8751                         if (typeof(cfg[ sizes[i] ] ) == 'undefined') {
8752                                 continue;
8753                         }
8754                         return cfg[ sizes[i] ];
8755                 }
8756                 return 1;
8757                 
8758     },
8759
8760     /**
8761      * Sets the width for a column.
8762      * @param {Number} col The column index
8763      * @param {Number} width The new width
8764      */
8765     setColumnWidth : function(col, width, suppressEvent){
8766         this.config[col].width = width;
8767         this.totalWidth = null;
8768         if(!suppressEvent){
8769              this.fireEvent("widthchange", this, col, width);
8770         }
8771     },
8772
8773     /**
8774      * Returns the total width of all columns.
8775      * @param {Boolean} includeHidden True to include hidden column widths
8776      * @return {Number}
8777      */
8778     getTotalWidth : function(includeHidden){
8779         if(!this.totalWidth){
8780             this.totalWidth = 0;
8781             for(var i = 0, len = this.config.length; i < len; i++){
8782                 if(includeHidden || !this.isHidden(i)){
8783                     this.totalWidth += this.getColumnWidth(i);
8784                 }
8785             }
8786         }
8787         return this.totalWidth;
8788     },
8789
8790     /**
8791      * Returns the header for the specified column.
8792      * @param {Number} col The column index
8793      * @return {String}
8794      */
8795     getColumnHeader : function(col){
8796         return this.config[col].header;
8797     },
8798
8799     /**
8800      * Sets the header for a column.
8801      * @param {Number} col The column index
8802      * @param {String} header The new header
8803      */
8804     setColumnHeader : function(col, header){
8805         this.config[col].header = header;
8806         this.fireEvent("headerchange", this, col, header);
8807     },
8808
8809     /**
8810      * Returns the tooltip for the specified column.
8811      * @param {Number} col The column index
8812      * @return {String}
8813      */
8814     getColumnTooltip : function(col){
8815             return this.config[col].tooltip;
8816     },
8817     /**
8818      * Sets the tooltip for a column.
8819      * @param {Number} col The column index
8820      * @param {String} tooltip The new tooltip
8821      */
8822     setColumnTooltip : function(col, tooltip){
8823             this.config[col].tooltip = tooltip;
8824     },
8825
8826     /**
8827      * Returns the dataIndex for the specified column.
8828      * @param {Number} col The column index
8829      * @return {Number}
8830      */
8831     getDataIndex : function(col){
8832         return this.config[col].dataIndex;
8833     },
8834
8835     /**
8836      * Sets the dataIndex for a column.
8837      * @param {Number} col The column index
8838      * @param {Number} dataIndex The new dataIndex
8839      */
8840     setDataIndex : function(col, dataIndex){
8841         this.config[col].dataIndex = dataIndex;
8842     },
8843
8844     
8845     
8846     /**
8847      * Returns true if the cell is editable.
8848      * @param {Number} colIndex The column index
8849      * @param {Number} rowIndex The row index - this is nto actually used..?
8850      * @return {Boolean}
8851      */
8852     isCellEditable : function(colIndex, rowIndex){
8853         return (this.config[colIndex].editable || (typeof this.config[colIndex].editable == "undefined" && this.config[colIndex].editor)) ? true : false;
8854     },
8855
8856     /**
8857      * Returns the editor defined for the cell/column.
8858      * return false or null to disable editing.
8859      * @param {Number} colIndex The column index
8860      * @param {Number} rowIndex The row index
8861      * @return {Object}
8862      */
8863     getCellEditor : function(colIndex, rowIndex){
8864         return this.config[colIndex].editor;
8865     },
8866
8867     /**
8868      * Sets if a column is editable.
8869      * @param {Number} col The column index
8870      * @param {Boolean} editable True if the column is editable
8871      */
8872     setEditable : function(col, editable){
8873         this.config[col].editable = editable;
8874     },
8875
8876
8877     /**
8878      * Returns true if the column is hidden.
8879      * @param {Number} colIndex The column index
8880      * @return {Boolean}
8881      */
8882     isHidden : function(colIndex){
8883         return this.config[colIndex].hidden;
8884     },
8885
8886
8887     /**
8888      * Returns true if the column width cannot be changed
8889      */
8890     isFixed : function(colIndex){
8891         return this.config[colIndex].fixed;
8892     },
8893
8894     /**
8895      * Returns true if the column can be resized
8896      * @return {Boolean}
8897      */
8898     isResizable : function(colIndex){
8899         return colIndex >= 0 && this.config[colIndex].resizable !== false && this.config[colIndex].fixed !== true;
8900     },
8901     /**
8902      * Sets if a column is hidden.
8903      * @param {Number} colIndex The column index
8904      * @param {Boolean} hidden True if the column is hidden
8905      */
8906     setHidden : function(colIndex, hidden){
8907         this.config[colIndex].hidden = hidden;
8908         this.totalWidth = null;
8909         this.fireEvent("hiddenchange", this, colIndex, hidden);
8910     },
8911
8912     /**
8913      * Sets the editor for a column.
8914      * @param {Number} col The column index
8915      * @param {Object} editor The editor object
8916      */
8917     setEditor : function(col, editor){
8918         this.config[col].editor = editor;
8919     },
8920     /**
8921      * Add a column (experimental...) - defaults to adding to the end..
8922      * @param {Object} config 
8923     */
8924     addColumn : function(c)
8925     {
8926     
8927         var i = this.config.length;
8928         this.config[i] = c;
8929         
8930         if(typeof c.dataIndex == "undefined"){
8931             c.dataIndex = i;
8932         }
8933         if(typeof c.renderer == "string"){
8934             c.renderer = Roo.util.Format[c.renderer];
8935         }
8936         if(typeof c.id == "undefined"){
8937             c.id = Roo.id();
8938         }
8939         if(c.editor && c.editor.xtype){
8940             c.editor  = Roo.factory(c.editor, Roo.grid);
8941         }
8942         if(c.editor && c.editor.isFormField){
8943             c.editor = new Roo.grid.GridEditor(c.editor);
8944         }
8945         this.lookup[c.id] = c;
8946     }
8947     
8948 });
8949
8950 Roo.grid.ColumnModel.defaultRenderer = function(value)
8951 {
8952     if(typeof value == "object") {
8953         return value;
8954     }
8955         if(typeof value == "string" && value.length < 1){
8956             return "&#160;";
8957         }
8958     
8959         return String.format("{0}", value);
8960 };
8961
8962 // Alias for backwards compatibility
8963 Roo.grid.DefaultColumnModel = Roo.grid.ColumnModel;
8964 /*
8965  * Based on:
8966  * Ext JS Library 1.1.1
8967  * Copyright(c) 2006-2007, Ext JS, LLC.
8968  *
8969  * Originally Released Under LGPL - original licence link has changed is not relivant.
8970  *
8971  * Fork - LGPL
8972  * <script type="text/javascript">
8973  */
8974  
8975 /**
8976  * @class Roo.LoadMask
8977  * A simple utility class for generically masking elements while loading data.  If the element being masked has
8978  * an underlying {@link Roo.data.Store}, the masking will be automatically synchronized with the store's loading
8979  * process and the mask element will be cached for reuse.  For all other elements, this mask will replace the
8980  * element's UpdateManager load indicator and will be destroyed after the initial load.
8981  * @constructor
8982  * Create a new LoadMask
8983  * @param {String/HTMLElement/Roo.Element} el The element or DOM node, or its id
8984  * @param {Object} config The config object
8985  */
8986 Roo.LoadMask = function(el, config){
8987     this.el = Roo.get(el);
8988     Roo.apply(this, config);
8989     if(this.store){
8990         this.store.on('beforeload', this.onBeforeLoad, this);
8991         this.store.on('load', this.onLoad, this);
8992         this.store.on('loadexception', this.onLoadException, this);
8993         this.removeMask = false;
8994     }else{
8995         var um = this.el.getUpdateManager();
8996         um.showLoadIndicator = false; // disable the default indicator
8997         um.on('beforeupdate', this.onBeforeLoad, this);
8998         um.on('update', this.onLoad, this);
8999         um.on('failure', this.onLoad, this);
9000         this.removeMask = true;
9001     }
9002 };
9003
9004 Roo.LoadMask.prototype = {
9005     /**
9006      * @cfg {Boolean} removeMask
9007      * True to create a single-use mask that is automatically destroyed after loading (useful for page loads),
9008      * False to persist the mask element reference for multiple uses (e.g., for paged data widgets).  Defaults to false.
9009      */
9010     removeMask : false,
9011     /**
9012      * @cfg {String} msg
9013      * The text to display in a centered loading message box (defaults to 'Loading...')
9014      */
9015     msg : 'Loading...',
9016     /**
9017      * @cfg {String} msgCls
9018      * The CSS class to apply to the loading message element (defaults to "x-mask-loading")
9019      */
9020     msgCls : 'x-mask-loading',
9021
9022     /**
9023      * Read-only. True if the mask is currently disabled so that it will not be displayed (defaults to false)
9024      * @type Boolean
9025      */
9026     disabled: false,
9027
9028     /**
9029      * Disables the mask to prevent it from being displayed
9030      */
9031     disable : function(){
9032        this.disabled = true;
9033     },
9034
9035     /**
9036      * Enables the mask so that it can be displayed
9037      */
9038     enable : function(){
9039         this.disabled = false;
9040     },
9041     
9042     onLoadException : function()
9043     {
9044         Roo.log(arguments);
9045         
9046         if (typeof(arguments[3]) != 'undefined') {
9047             Roo.MessageBox.alert("Error loading",arguments[3]);
9048         } 
9049         /*
9050         try {
9051             if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
9052                 Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
9053             }   
9054         } catch(e) {
9055             
9056         }
9057         */
9058     
9059         (function() { this.el.unmask(this.removeMask); }).defer(50, this);
9060     },
9061     // private
9062     onLoad : function()
9063     {
9064         (function() { this.el.unmask(this.removeMask); }).defer(50, this);
9065     },
9066
9067     // private
9068     onBeforeLoad : function(){
9069         if(!this.disabled){
9070             (function() { this.el.mask(this.msg, this.msgCls); }).defer(50, this);
9071         }
9072     },
9073
9074     // private
9075     destroy : function(){
9076         if(this.store){
9077             this.store.un('beforeload', this.onBeforeLoad, this);
9078             this.store.un('load', this.onLoad, this);
9079             this.store.un('loadexception', this.onLoadException, this);
9080         }else{
9081             var um = this.el.getUpdateManager();
9082             um.un('beforeupdate', this.onBeforeLoad, this);
9083             um.un('update', this.onLoad, this);
9084             um.un('failure', this.onLoad, this);
9085         }
9086     }
9087 };/**
9088  * @class Roo.bootstrap.Table
9089  * @licence LGBL
9090  * @extends Roo.bootstrap.Component
9091  * @children Roo.bootstrap.TableBody
9092  * Bootstrap Table class.  This class represents the primary interface of a component based grid control.
9093  * Similar to Roo.grid.Grid
9094  * <pre><code>
9095  var table = Roo.factory({
9096     xtype : 'Table',
9097     xns : Roo.bootstrap,
9098     autoSizeColumns: true,
9099     
9100     
9101     store : {
9102         xtype : 'Store',
9103         xns : Roo.data,
9104         remoteSort : true,
9105         sortInfo : { direction : 'ASC', field: 'name' },
9106         proxy : {
9107            xtype : 'HttpProxy',
9108            xns : Roo.data,
9109            method : 'GET',
9110            url : 'https://example.com/some.data.url.json'
9111         },
9112         reader : {
9113            xtype : 'JsonReader',
9114            xns : Roo.data,
9115            fields : [ 'id', 'name', whatever' ],
9116            id : 'id',
9117            root : 'data'
9118         }
9119     },
9120     cm : [
9121         {
9122             xtype : 'ColumnModel',
9123             xns : Roo.grid,
9124             align : 'center',
9125             cursor : 'pointer',
9126             dataIndex : 'is_in_group',
9127             header : "Name",
9128             sortable : true,
9129             renderer : function(v, x , r) {  
9130             
9131                 return String.format("{0}", v)
9132             }
9133             width : 3
9134         } // more columns..
9135     ],
9136     selModel : {
9137         xtype : 'RowSelectionModel',
9138         xns : Roo.bootstrap.Table
9139         // you can add listeners to catch selection change here....
9140     }
9141      
9142
9143  });
9144  // set any options
9145  grid.render(Roo.get("some-div"));
9146 </code></pre>
9147
9148 Currently the Table  uses multiple headers to try and handle XL / Medium etc... styling
9149
9150
9151
9152  *
9153  * @cfg {Roo.grid.AbstractSelectionModel} sm The selection model to use (cell selection is not supported yet)
9154  * @cfg {Roo.data.Store} store The data store to use
9155  * @cfg {Roo.grid.ColumnModel} cm[] A column for the grid.
9156  * 
9157  * @cfg {String} cls table class
9158  *
9159  *
9160  * @cfg {string} empty_results  Text to display for no results 
9161  * @cfg {boolean} striped Should the rows be alternative striped
9162  * @cfg {boolean} bordered Add borders to the table
9163  * @cfg {boolean} hover Add hover highlighting
9164  * @cfg {boolean} condensed Format condensed
9165  * @cfg {boolean} responsive default false - if this is on, columns are rendered with col-xs-4 etc. classes, otherwise columns will be sized by CSS,
9166  *                also adds table-responsive (see bootstrap docs for details)
9167  * @cfg {Boolean} loadMask (true|false) default false
9168  * @cfg {Boolean} footerShow (true|false) generate tfoot, default true
9169  * @cfg {Boolean} footerRow (true|false) generate tfoot with columns of values, default false
9170  * @cfg {Boolean} headerShow (true|false) generate thead, default true
9171  * @cfg {Boolean} rowSelection (true|false) default false
9172  * @cfg {Boolean} cellSelection (true|false) default false
9173  * @cfg {Boolean} scrollBody (true|false) default false - body scrolled / fixed header (with resizable columns)
9174  * @cfg {Roo.bootstrap.PagingToolbar} footer  a paging toolbar
9175  * @cfg {Boolean} lazyLoad  auto load data while scrolling to the end (default false)
9176  * @cfg {Boolean} auto_hide_footer  auto hide footer if only one page (default false)
9177  * @cfg {Boolean} enableColumnResize default true if columns can be resized = needs scrollBody to be set to work (drag/drop)
9178  * @cfg {Boolean} disableAutoSize disable autoSize() and initCSS()
9179  *
9180  * 
9181  * @cfg {Number} minColumnWidth default 50 pixels minimum column width 
9182  * 
9183  * @constructor
9184  * Create a new Table
9185  * @param {Object} config The config object
9186  */
9187
9188 Roo.bootstrap.Table = function(config)
9189 {
9190     Roo.bootstrap.Table.superclass.constructor.call(this, config);
9191      
9192     // BC...
9193     this.rowSelection = (typeof(config.rowSelection) != 'undefined') ? config.rowSelection : this.rowSelection;
9194     this.cellSelection = (typeof(config.cellSelection) != 'undefined') ? config.cellSelection : this.cellSelection;
9195     this.headerShow = (typeof(config.thead) != 'undefined') ? config.thead : this.headerShow;
9196     this.footerShow = (typeof(config.tfoot) != 'undefined') ? config.tfoot : this.footerShow;
9197     
9198     this.view = this; // compat with grid.
9199     
9200     this.sm = this.sm || {xtype: 'RowSelectionModel'};
9201     if (this.sm) {
9202         this.sm.grid = this;
9203         this.selModel = Roo.factory(this.sm, Roo.grid);
9204         this.sm = this.selModel;
9205         this.sm.xmodule = this.xmodule || false;
9206     }
9207     
9208     if (this.cm && typeof(this.cm.config) == 'undefined') {
9209         this.colModel = new Roo.grid.ColumnModel(this.cm);
9210         this.cm = this.colModel;
9211         this.cm.xmodule = this.xmodule || false;
9212     }
9213     if (this.store) {
9214         this.store= Roo.factory(this.store, Roo.data);
9215         this.ds = this.store;
9216         this.ds.xmodule = this.xmodule || false;
9217          
9218     }
9219     if (this.footer && this.store) {
9220         this.footer.dataSource = this.ds;
9221         this.footer = Roo.factory(this.footer);
9222     }
9223     
9224     /** @private */
9225     this.addEvents({
9226         /**
9227          * @event cellclick
9228          * Fires when a cell is clicked
9229          * @param {Roo.bootstrap.Table} this
9230          * @param {Roo.Element} el
9231          * @param {Number} rowIndex
9232          * @param {Number} columnIndex
9233          * @param {Roo.EventObject} e
9234          */
9235         "cellclick" : true,
9236         /**
9237          * @event celldblclick
9238          * Fires when a cell is double clicked
9239          * @param {Roo.bootstrap.Table} this
9240          * @param {Roo.Element} el
9241          * @param {Number} rowIndex
9242          * @param {Number} columnIndex
9243          * @param {Roo.EventObject} e
9244          */
9245         "celldblclick" : true,
9246         /**
9247          * @event rowclick
9248          * Fires when a row is clicked
9249          * @param {Roo.bootstrap.Table} this
9250          * @param {Roo.Element} el
9251          * @param {Number} rowIndex
9252          * @param {Roo.EventObject} e
9253          */
9254         "rowclick" : true,
9255         /**
9256          * @event rowdblclick
9257          * Fires when a row is double clicked
9258          * @param {Roo.bootstrap.Table} this
9259          * @param {Roo.Element} el
9260          * @param {Number} rowIndex
9261          * @param {Roo.EventObject} e
9262          */
9263         "rowdblclick" : true,
9264         /**
9265          * @event mouseover
9266          * Fires when a mouseover occur
9267          * @param {Roo.bootstrap.Table} this
9268          * @param {Roo.Element} el
9269          * @param {Number} rowIndex
9270          * @param {Number} columnIndex
9271          * @param {Roo.EventObject} e
9272          */
9273         "mouseover" : true,
9274         /**
9275          * @event mouseout
9276          * Fires when a mouseout occur
9277          * @param {Roo.bootstrap.Table} this
9278          * @param {Roo.Element} el
9279          * @param {Number} rowIndex
9280          * @param {Number} columnIndex
9281          * @param {Roo.EventObject} e
9282          */
9283         "mouseout" : true,
9284         /**
9285          * @event rowclass
9286          * Fires when a row is rendered, so you can change add a style to it.
9287          * @param {Roo.bootstrap.Table} this
9288          * @param {Object} rowcfg   contains record  rowIndex colIndex and rowClass - set rowClass to add a style.
9289          */
9290         'rowclass' : true,
9291           /**
9292          * @event rowsrendered
9293          * Fires when all the  rows have been rendered
9294          * @param {Roo.bootstrap.Table} this
9295          */
9296         'rowsrendered' : true,
9297         /**
9298          * @event contextmenu
9299          * The raw contextmenu event for the entire grid.
9300          * @param {Roo.EventObject} e
9301          */
9302         "contextmenu" : true,
9303         /**
9304          * @event rowcontextmenu
9305          * Fires when a row is right clicked
9306          * @param {Roo.bootstrap.Table} this
9307          * @param {Number} rowIndex
9308          * @param {Roo.EventObject} e
9309          */
9310         "rowcontextmenu" : true,
9311         /**
9312          * @event cellcontextmenu
9313          * Fires when a cell is right clicked
9314          * @param {Roo.bootstrap.Table} this
9315          * @param {Number} rowIndex
9316          * @param {Number} cellIndex
9317          * @param {Roo.EventObject} e
9318          */
9319          "cellcontextmenu" : true,
9320          /**
9321          * @event headercontextmenu
9322          * Fires when a header is right clicked
9323          * @param {Roo.bootstrap.Table} this
9324          * @param {Number} columnIndex
9325          * @param {Roo.EventObject} e
9326          */
9327         "headercontextmenu" : true,
9328         /**
9329          * @event mousedown
9330          * The raw mousedown event for the entire grid.
9331          * @param {Roo.EventObject} e
9332          */
9333         "mousedown" : true
9334         
9335     });
9336 };
9337
9338 Roo.extend(Roo.bootstrap.Table, Roo.bootstrap.Component,  {
9339     
9340     cls: false,
9341     
9342     empty_results : '',
9343     striped : false,
9344     scrollBody : false,
9345     bordered: false,
9346     hover:  false,
9347     condensed : false,
9348     responsive : false,
9349     sm : false,
9350     cm : false,
9351     store : false,
9352     loadMask : false,
9353     footerShow : true,
9354     footerRow : false,
9355     headerShow : true,
9356     enableColumnResize: true,
9357     disableAutoSize: false,
9358   
9359     rowSelection : false,
9360     cellSelection : false,
9361     layout : false,
9362
9363     minColumnWidth : 50,
9364     
9365     // Roo.Element - the tbody
9366     bodyEl: false,  // <tbody> Roo.Element - thead element    
9367     headEl: false,  // <thead> Roo.Element - thead element
9368     resizeProxy : false, // proxy element for dragging?
9369
9370
9371     
9372     container: false, // used by gridpanel...
9373     
9374     lazyLoad : false,
9375     
9376     CSS : Roo.util.CSS,
9377     
9378     auto_hide_footer : false,
9379     
9380     view: false, // actually points to this..
9381     
9382     getAutoCreate : function()
9383     {
9384         var cfg = Roo.apply({}, Roo.bootstrap.Table.superclass.getAutoCreate.call(this));
9385         
9386         cfg = {
9387             tag: 'table',
9388             cls : 'table', 
9389             cn : []
9390         };
9391         // this get's auto added by panel.Grid
9392         if (this.scrollBody) {
9393             cfg.cls += ' table-body-fixed';
9394         }    
9395         if (this.striped) {
9396             cfg.cls += ' table-striped';
9397         }
9398         
9399         if (this.hover) {
9400             cfg.cls += ' table-hover';
9401         }
9402         if (this.bordered) {
9403             cfg.cls += ' table-bordered';
9404         }
9405         if (this.condensed) {
9406             cfg.cls += ' table-condensed';
9407         }
9408         
9409         if (this.responsive) {
9410             cfg.cls += ' table-responsive';
9411         }
9412         
9413         if (this.cls) {
9414             cfg.cls+=  ' ' +this.cls;
9415         }
9416         
9417         
9418         
9419         if (this.layout) {
9420             cfg.style = (typeof(cfg.style) == 'undefined') ? ('table-layout:' + this.layout + ';') : (cfg.style + ('table-layout:' + this.layout + ';'));
9421         }
9422         
9423         if(this.store || this.cm){
9424             if(this.headerShow){
9425                 cfg.cn.push(this.renderHeader());
9426             }
9427             
9428             cfg.cn.push(this.renderBody());
9429             
9430             if(this.footerShow || this.footerRow){
9431                 cfg.cn.push(this.renderFooter());
9432             }
9433
9434             // where does this come from?
9435             //cfg.cls+=  ' TableGrid';
9436         }
9437         
9438         return { cn : [ cfg ] };
9439     },
9440     
9441     initEvents : function()
9442     {   
9443         if(!this.store || !this.cm){
9444             return;
9445         }
9446         if (this.selModel) {
9447             this.selModel.initEvents();
9448         }
9449         
9450         
9451         //Roo.log('initEvents with ds!!!!');
9452         
9453         this.bodyEl = this.el.select('tbody', true).first();
9454         this.headEl = this.el.select('thead', true).first();
9455         this.mainFoot = this.el.select('tfoot', true).first();
9456         
9457         
9458         
9459         
9460         Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
9461             e.on('click', this.sort, this);
9462         }, this);
9463         
9464         
9465         // why is this done????? = it breaks dialogs??
9466         //this.parent().el.setStyle('position', 'relative');
9467         
9468         
9469         if (this.footer) {
9470             this.footer.parentId = this.id;
9471             this.footer.onRender(this.el.select('tfoot tr td').first(), null);
9472             
9473             if(this.lazyLoad){
9474                 this.el.select('tfoot tr td').first().addClass('hide');
9475             }
9476         } 
9477         
9478         if(this.loadMask) {
9479             this.maskEl = new Roo.LoadMask(this.el, { store : this.ds, msgCls: 'roo-el-mask-msg' });
9480         }
9481         
9482         this.store.on('load', this.onLoad, this);
9483         this.store.on('beforeload', this.onBeforeLoad, this);
9484         this.store.on('update', this.onUpdate, this);
9485         this.store.on('add', this.onAdd, this);
9486         this.store.on("clear", this.clear, this);
9487         
9488         this.el.on("contextmenu", this.onContextMenu, this);
9489         
9490         
9491         this.cm.on("headerchange", this.onHeaderChange, this);
9492         this.cm.on("hiddenchange", this.onHiddenChange, this, arguments);
9493
9494  //?? does bodyEl get replaced on render?
9495         this.bodyEl.on("click", this.onClick, this);
9496         this.bodyEl.on("dblclick", this.onDblClick, this);        
9497         this.bodyEl.on('scroll', this.onBodyScroll, this);
9498
9499         // guessing mainbody will work - this relays usually caught by selmodel at present.
9500         this.relayEvents(this.bodyEl, ["mousedown","mouseup","mouseover","mouseout","keypress"]);
9501   
9502   
9503         this.resizeProxy = Roo.get(document.body).createChild({ cls:"x-grid-resize-proxy", html: '&#160;' });
9504         
9505   
9506         if(this.headEl && this.enableColumnResize !== false && Roo.grid.SplitDragZone){
9507             new Roo.grid.SplitDragZone(this, this.headEl.dom, false); // not sure what 'lockedHd is for this implementation..)
9508         }
9509         
9510         this.initCSS();
9511     },
9512     // Compatibility with grid - we implement all the view features at present.
9513     getView : function()
9514     {
9515         return this;
9516     },
9517     
9518     initCSS : function()
9519     {
9520         if(this.disableAutoSize) {
9521             return;
9522         }
9523         
9524         var cm = this.cm, styles = [];
9525         this.CSS.removeStyleSheet(this.id + '-cssrules');
9526         var headHeight = this.headEl ? this.headEl.dom.clientHeight : 0;
9527         // we can honour xs/sm/md/xl  as widths...
9528         // we first have to decide what widht we are currently at...
9529         var sz = Roo.getGridSize();
9530         
9531         var total = 0;
9532         var last = -1;
9533         var cols = []; // visable cols.
9534         var total_abs = 0;
9535         for(var i = 0, len = cm.getColumnCount(); i < len; i++) {
9536             var w = cm.getColumnWidth(i, false);
9537             if(cm.isHidden(i)){
9538                 cols.push( { rel : false, abs : 0 });
9539                 continue;
9540             }
9541             if (w !== false) {
9542                 cols.push( { rel : false, abs : w });
9543                 total_abs += w;
9544                 last = i; // not really..
9545                 continue;
9546             }
9547             var w = cm.getColumnWidth(i, sz);
9548             if (w > 0) {
9549                 last = i
9550             }
9551             total += w;
9552             cols.push( { rel : w, abs : false });
9553         }
9554         
9555         var avail = this.bodyEl.dom.clientWidth - total_abs;
9556         
9557         var unitWidth = Math.floor(avail / total);
9558         var rem = avail - (unitWidth * total);
9559         
9560         var hidden, width, pos = 0 , splithide , left;
9561         for(var i = 0, len = cm.getColumnCount(); i < len; i++) {
9562             
9563             hidden = 'display:none;';
9564             left = '';
9565             width  = 'width:0px;';
9566             splithide = '';
9567             if(!cm.isHidden(i)){
9568                 hidden = '';
9569                 
9570                 
9571                 // we can honour xs/sm/md/xl ?
9572                 var w = cols[i].rel == false ? cols[i].abs : (cols[i].rel * unitWidth);
9573                 if (w===0) {
9574                     hidden = 'display:none;';
9575                 }
9576                 // width should return a small number...
9577                 if (i == last) {
9578                     w+=rem; // add the remaining with..
9579                 }
9580                 pos += w;
9581                 left = "left:" + (pos -4) + "px;";
9582                 width = "width:" + w+ "px;";
9583                 
9584             }
9585             if (this.responsive) {
9586                 width = '';
9587                 left = '';
9588                 hidden = cm.isHidden(i) ? 'display:none;' : '';
9589                 splithide = 'display: none;';
9590             }
9591             
9592             styles.push( '#' , this.id , ' .x-col-' , i, " {", cm.config[i].css, width, hidden, "}\n" );
9593             if (this.headEl) {
9594                 if (i == last) {
9595                     splithide = 'display:none;';
9596                 }
9597                 
9598                 styles.push('#' , this.id , ' .x-hcol-' , i, " { ", width, hidden," }\n",
9599                             '#' , this.id , ' .x-grid-split-' , i, " { ", left, splithide, 'height:', (headHeight - 4), "px;}\n",
9600                             // this is the popover version..
9601                             '.popover-inner #' , this.id , ' .x-grid-split-' , i, " { ", left, splithide, 'height:', 100, "%;}\n"
9602                 );
9603             }
9604             
9605         }
9606         //Roo.log(styles.join(''));
9607         this.CSS.createStyleSheet( styles.join(''), this.id + '-cssrules');
9608         
9609     },
9610     
9611     
9612     
9613     onContextMenu : function(e, t)
9614     {
9615         this.processEvent("contextmenu", e);
9616     },
9617     
9618     processEvent : function(name, e)
9619     {
9620         if (name != 'touchstart' ) {
9621             this.fireEvent(name, e);    
9622         }
9623         
9624         var t = e.getTarget();
9625         
9626         var cell = Roo.get(t);
9627         
9628         if(!cell){
9629             return;
9630         }
9631         
9632         if(cell.findParent('tfoot', false, true)){
9633             return;
9634         }
9635         
9636         if(cell.findParent('thead', false, true)){
9637             
9638             if(e.getTarget().nodeName.toLowerCase() != 'th'){
9639                 cell = Roo.get(t).findParent('th', false, true);
9640                 if (!cell) {
9641                     Roo.log("failed to find th in thead?");
9642                     Roo.log(e.getTarget());
9643                     return;
9644                 }
9645             }
9646             
9647             var cellIndex = cell.dom.cellIndex;
9648             
9649             var ename = name == 'touchstart' ? 'click' : name;
9650             this.fireEvent("header" + ename, this, cellIndex, e);
9651             
9652             return;
9653         }
9654         
9655         if(e.getTarget().nodeName.toLowerCase() != 'td'){
9656             cell = Roo.get(t).findParent('td', false, true);
9657             if (!cell) {
9658                 Roo.log("failed to find th in tbody?");
9659                 Roo.log(e.getTarget());
9660                 return;
9661             }
9662         }
9663         
9664         var row = cell.findParent('tr', false, true);
9665         var cellIndex = cell.dom.cellIndex;
9666         var rowIndex = row.dom.rowIndex - 1;
9667         
9668         if(row !== false){
9669             
9670             this.fireEvent("row" + name, this, rowIndex, e);
9671             
9672             if(cell !== false){
9673             
9674                 this.fireEvent("cell" + name, this, rowIndex, cellIndex, e);
9675             }
9676         }
9677         
9678     },
9679     
9680     onMouseover : function(e, el)
9681     {
9682         var cell = Roo.get(el);
9683         
9684         if(!cell){
9685             return;
9686         }
9687         
9688         if(e.getTarget().nodeName.toLowerCase() != 'td'){
9689             cell = cell.findParent('td', false, true);
9690         }
9691         
9692         var row = cell.findParent('tr', false, true);
9693         var cellIndex = cell.dom.cellIndex;
9694         var rowIndex = row.dom.rowIndex - 1; // start from 0
9695         
9696         this.fireEvent('mouseover', this, cell, rowIndex, cellIndex, e);
9697         
9698     },
9699     
9700     onMouseout : function(e, el)
9701     {
9702         var cell = Roo.get(el);
9703         
9704         if(!cell){
9705             return;
9706         }
9707         
9708         if(e.getTarget().nodeName.toLowerCase() != 'td'){
9709             cell = cell.findParent('td', false, true);
9710         }
9711         
9712         var row = cell.findParent('tr', false, true);
9713         var cellIndex = cell.dom.cellIndex;
9714         var rowIndex = row.dom.rowIndex - 1; // start from 0
9715         
9716         this.fireEvent('mouseout', this, cell, rowIndex, cellIndex, e);
9717         
9718     },
9719     
9720     onClick : function(e, el)
9721     {
9722         var cell = Roo.get(el);
9723         
9724         if(!cell || (!this.cellSelection && !this.rowSelection)){
9725             return;
9726         }
9727         
9728         if(e.getTarget().nodeName.toLowerCase() != 'td'){
9729             cell = cell.findParent('td', false, true);
9730         }
9731         
9732         if(!cell || typeof(cell) == 'undefined'){
9733             return;
9734         }
9735         
9736         var row = cell.findParent('tr', false, true);
9737         
9738         if(!row || typeof(row) == 'undefined'){
9739             return;
9740         }
9741         
9742         var cellIndex = cell.dom.cellIndex;
9743         var rowIndex = this.getRowIndex(row);
9744         
9745         // why??? - should these not be based on SelectionModel?
9746         //if(this.cellSelection){
9747             this.fireEvent('cellclick', this, cell, rowIndex, cellIndex, e);
9748         //}
9749         
9750         //if(this.rowSelection){
9751             this.fireEvent('rowclick', this, row, rowIndex, e);
9752         //}
9753          
9754     },
9755         
9756     onDblClick : function(e,el)
9757     {
9758         var cell = Roo.get(el);
9759         
9760         if(!cell || (!this.cellSelection && !this.rowSelection)){
9761             return;
9762         }
9763         
9764         if(e.getTarget().nodeName.toLowerCase() != 'td'){
9765             cell = cell.findParent('td', false, true);
9766         }
9767         
9768         if(!cell || typeof(cell) == 'undefined'){
9769             return;
9770         }
9771         
9772         var row = cell.findParent('tr', false, true);
9773         
9774         if(!row || typeof(row) == 'undefined'){
9775             return;
9776         }
9777         
9778         var cellIndex = cell.dom.cellIndex;
9779         var rowIndex = this.getRowIndex(row);
9780         
9781         if(this.cellSelection){
9782             this.fireEvent('celldblclick', this, cell, rowIndex, cellIndex, e);
9783         }
9784         
9785         if(this.rowSelection){
9786             this.fireEvent('rowdblclick', this, row, rowIndex, e);
9787         }
9788     },
9789     findRowIndex : function(el)
9790     {
9791         var cell = Roo.get(el);
9792         if(!cell) {
9793             return false;
9794         }
9795         var row = cell.findParent('tr', false, true);
9796         
9797         if(!row || typeof(row) == 'undefined'){
9798             return false;
9799         }
9800         return this.getRowIndex(row);
9801     },
9802     sort : function(e,el)
9803     {
9804         var col = Roo.get(el);
9805         
9806         if(!col.hasClass('sortable')){
9807             return;
9808         }
9809         
9810         var sort = col.attr('sort');
9811         var dir = 'ASC';
9812         
9813         if(col.select('i', true).first().hasClass('fa-arrow-up')){
9814             dir = 'DESC';
9815         }
9816         
9817         this.store.sortInfo = {field : sort, direction : dir};
9818         
9819         if (this.footer) {
9820             Roo.log("calling footer first");
9821             this.footer.onClick('first');
9822         } else {
9823         
9824             this.store.load({ params : { start : 0 } });
9825         }
9826     },
9827     
9828     renderHeader : function()
9829     {
9830         var header = {
9831             tag: 'thead',
9832             cn : []
9833         };
9834         
9835         var cm = this.cm;
9836         this.totalWidth = 0;
9837         
9838         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
9839             
9840             var config = cm.config[i];
9841             
9842             var c = {
9843                 tag: 'th',
9844                 cls : 'x-hcol-' + i,
9845                 style : '',
9846                 
9847                 html: cm.getColumnHeader(i)
9848             };
9849             
9850             var tooltip = cm.getColumnTooltip(i);
9851             if (tooltip) {
9852                 c.tooltip = tooltip;
9853             }
9854             
9855             
9856             var hh = '';
9857             
9858             if(typeof(config.sortable) != 'undefined' && config.sortable){
9859                 c.cls += ' sortable';
9860                 c.html = '<i class="fa"></i>' + c.html;
9861             }
9862             
9863             // could use BS4 hidden-..-down 
9864             
9865             if(typeof(config.lgHeader) != 'undefined'){
9866                 hh += '<span class="hidden-xs hidden-sm hidden-md ">' + config.lgHeader + '</span>';
9867             }
9868             
9869             if(typeof(config.mdHeader) != 'undefined'){
9870                 hh += '<span class="hidden-xs hidden-sm hidden-lg">' + config.mdHeader + '</span>';
9871             }
9872             
9873             if(typeof(config.smHeader) != 'undefined'){
9874                 hh += '<span class="hidden-xs hidden-md hidden-lg">' + config.smHeader + '</span>';
9875             }
9876             
9877             if(typeof(config.xsHeader) != 'undefined'){
9878                 hh += '<span class="hidden-sm hidden-md hidden-lg">' + config.xsHeader + '</span>';
9879             }
9880             
9881             if(hh.length){
9882                 c.html = hh;
9883             }
9884             
9885             if(typeof(config.tooltip) != 'undefined'){
9886                 c.tooltip = config.tooltip;
9887             }
9888             
9889             if(typeof(config.colspan) != 'undefined'){
9890                 c.colspan = config.colspan;
9891             }
9892             
9893             // hidden is handled by CSS now
9894             
9895             if(typeof(config.dataIndex) != 'undefined'){
9896                 c.sort = config.dataIndex;
9897             }
9898             
9899            
9900             
9901             if(typeof(config.align) != 'undefined' && config.align.length){
9902                 c.style += ' text-align:' + config.align + ';';
9903             }
9904             
9905             /* width is done in CSS
9906              *if(typeof(config.width) != 'undefined'){
9907                 c.style += ' width:' + config.width + 'px;';
9908                 this.totalWidth += config.width;
9909             } else {
9910                 this.totalWidth += 100; // assume minimum of 100 per column?
9911             }
9912             */
9913             
9914             if(typeof(config.cls) != 'undefined'){
9915                 c.cls = (typeof(c.cls) == 'undefined') ? config.cls : (c.cls + ' ' + config.cls);
9916             }
9917             // this is the bit that doesnt reall work at all...
9918             
9919             if (this.responsive) {
9920                  
9921             
9922                 ['xs','sm','md','lg'].map(function(size){
9923                     
9924                     if(typeof(config[size]) == 'undefined'){
9925                         return;
9926                     }
9927                      
9928                     if (!config[size]) { // 0 = hidden
9929                         // BS 4 '0' is treated as hide that column and below.
9930                         c.cls += ' hidden-' + size + ' hidden' + size + '-down';
9931                         return;
9932                     }
9933                     
9934                     c.cls += ' col-' + size + '-' + config[size] + (
9935                         size == 'xs' ? (' col-' + config[size] ) : '' // bs4 col-{num} replaces col-xs
9936                     );
9937                     
9938                     
9939                 });
9940             }
9941             // at the end?
9942             
9943             c.html +=' <span class="x-grid-split x-grid-split-' + i + '"></span>';
9944             
9945             
9946             
9947             
9948             header.cn.push(c)
9949         }
9950         
9951         return header;
9952     },
9953     
9954     renderBody : function()
9955     {
9956         var body = {
9957             tag: 'tbody',
9958             cn : [
9959                 {
9960                     tag: 'tr',
9961                     cn : [
9962                         {
9963                             tag : 'td',
9964                             colspan :  this.cm.getColumnCount()
9965                         }
9966                     ]
9967                 }
9968             ]
9969         };
9970         
9971         return body;
9972     },
9973     
9974     renderFooter : function()
9975     {
9976         var footer = {
9977             tag: 'tfoot',
9978             cn : [
9979                 {
9980                     tag: 'tr',
9981                     cn : [
9982                         {
9983                             tag : 'td',
9984                             colspan :  this.cm.getColumnCount()
9985                         }
9986                     ]
9987                 }
9988             ]
9989         };
9990         
9991         return footer;
9992     },
9993     
9994     onLoad : function()
9995     {
9996 //        Roo.log('ds onload');
9997         this.clear();
9998         
9999         var _this = this;
10000         var cm = this.cm;
10001         var ds = this.store;
10002         
10003         Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
10004             e.select('i', true).removeClass(['fa-arrow-up', 'fa-arrow-down']);
10005             if (_this.store.sortInfo) {
10006                     
10007                 if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'ASC'){
10008                     e.select('i', true).addClass(['fa-arrow-up']);
10009                 }
10010                 
10011                 if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'DESC'){
10012                     e.select('i', true).addClass(['fa-arrow-down']);
10013                 }
10014             }
10015         });
10016         
10017         var tbody =  this.bodyEl;
10018               
10019         if(ds.getCount() > 0){
10020             ds.data.each(function(d,rowIndex){
10021                 var row =  this.renderRow(cm, ds, rowIndex);
10022                 
10023                 tbody.createChild(row);
10024                 
10025                 var _this = this;
10026                 
10027                 if(row.cellObjects.length){
10028                     Roo.each(row.cellObjects, function(r){
10029                         _this.renderCellObject(r);
10030                     })
10031                 }
10032                 
10033             }, this);
10034         } else if (this.empty_results.length) {
10035             this.el.mask(this.empty_results, 'no-spinner');
10036         }
10037         
10038         var tfoot = this.el.select('tfoot', true).first();
10039         
10040         if(this.footerShow && !this.footerRow && this.auto_hide_footer && this.mainFoot){
10041             
10042             this.mainFoot.setVisibilityMode(Roo.Element.DISPLAY).hide();
10043             
10044             var total = this.ds.getTotalCount();
10045             
10046             if(this.footer.pageSize < total){
10047                 this.mainFoot.show();
10048             }
10049         }
10050
10051         if(!this.footerShow && this.footerRow) {
10052
10053             var tr = {
10054                 tag : 'tr',
10055                 cn : []
10056             };
10057
10058             for(var i = 0, len = cm.getColumnCount(); i < len; i++){
10059                 var footer = typeof(cm.config[i].footer) == "function" ? cm.config[i].footer(ds, cm.config[i]) : cm.config[i].footer;
10060                 var td = {
10061                     tag: 'td',
10062                     cls : ' x-fcol-' + i,
10063                     html: footer
10064                 };
10065
10066                 tr.cn.push(td);
10067                 
10068             }
10069             
10070             tfoot.dom.innerHTML = '';
10071
10072             tfoot.createChild(tr);
10073         }
10074         
10075         Roo.each(this.el.select('tbody td', true).elements, function(e){
10076             e.on('mouseover', _this.onMouseover, _this);
10077         });
10078         
10079         Roo.each(this.el.select('tbody td', true).elements, function(e){
10080             e.on('mouseout', _this.onMouseout, _this);
10081         });
10082         this.fireEvent('rowsrendered', this);
10083         
10084         this.autoSize();
10085         
10086         this.initCSS(); /// resize cols
10087
10088         
10089     },
10090     
10091     
10092     onUpdate : function(ds,record)
10093     {
10094         this.refreshRow(record);
10095         this.autoSize();
10096     },
10097     
10098     onRemove : function(ds, record, index, isUpdate){
10099         if(isUpdate !== true){
10100             this.fireEvent("beforerowremoved", this, index, record);
10101         }
10102         var bt = this.bodyEl.dom;
10103         
10104         var rows = this.el.select('tbody > tr', true).elements;
10105         
10106         if(typeof(rows[index]) != 'undefined'){
10107             bt.removeChild(rows[index].dom);
10108         }
10109         
10110 //        if(bt.rows[index]){
10111 //            bt.removeChild(bt.rows[index]);
10112 //        }
10113         
10114         if(isUpdate !== true){
10115             //this.stripeRows(index);
10116             //this.syncRowHeights(index, index);
10117             //this.layout();
10118             this.fireEvent("rowremoved", this, index, record);
10119         }
10120     },
10121     
10122     onAdd : function(ds, records, rowIndex)
10123     {
10124         //Roo.log('on Add called');
10125         // - note this does not handle multiple adding very well..
10126         var bt = this.bodyEl.dom;
10127         for (var i =0 ; i < records.length;i++) {
10128             //Roo.log('call insert row Add called on ' + rowIndex + ':' + i);
10129             //Roo.log(records[i]);
10130             //Roo.log(this.store.getAt(rowIndex+i));
10131             this.insertRow(this.store, rowIndex + i, false);
10132             return;
10133         }
10134         
10135     },
10136     
10137     
10138     refreshRow : function(record){
10139         var ds = this.store, index;
10140         if(typeof record == 'number'){
10141             index = record;
10142             record = ds.getAt(index);
10143         }else{
10144             index = ds.indexOf(record);
10145             if (index < 0) {
10146                 return; // should not happen - but seems to 
10147             }
10148         }
10149         this.insertRow(ds, index, true);
10150         this.autoSize();
10151         this.onRemove(ds, record, index+1, true);
10152         this.autoSize();
10153         //this.syncRowHeights(index, index);
10154         //this.layout();
10155         this.fireEvent("rowupdated", this, index, record);
10156     },
10157     // private - called by RowSelection
10158     onRowSelect : function(rowIndex){
10159         var row = this.getRowDom(rowIndex);
10160         row.addClass(['bg-info','info']);
10161     },
10162     // private - called by RowSelection
10163     onRowDeselect : function(rowIndex)
10164     {
10165         if (rowIndex < 0) {
10166             return;
10167         }
10168         var row = this.getRowDom(rowIndex);
10169         row.removeClass(['bg-info','info']);
10170     },
10171       /**
10172      * Focuses the specified row.
10173      * @param {Number} row The row index
10174      */
10175     focusRow : function(row)
10176     {
10177         //Roo.log('GridView.focusRow');
10178         var x = this.bodyEl.dom.scrollLeft;
10179         this.focusCell(row, 0, false);
10180         this.bodyEl.dom.scrollLeft = x;
10181
10182     },
10183      /**
10184      * Focuses the specified cell.
10185      * @param {Number} row The row index
10186      * @param {Number} col The column index
10187      * @param {Boolean} hscroll false to disable horizontal scrolling
10188      */
10189     focusCell : function(row, col, hscroll)
10190     {
10191         //Roo.log('GridView.focusCell');
10192         var el = this.ensureVisible(row, col, hscroll);
10193         // not sure what focusEL achives = it's a <a> pos relative 
10194         //this.focusEl.alignTo(el, "tl-tl");
10195         //if(Roo.isGecko){
10196         //    this.focusEl.focus();
10197         //}else{
10198         //    this.focusEl.focus.defer(1, this.focusEl);
10199         //}
10200     },
10201     
10202      /**
10203      * Scrolls the specified cell into view
10204      * @param {Number} row The row index
10205      * @param {Number} col The column index
10206      * @param {Boolean} hscroll false to disable horizontal scrolling
10207      */
10208     ensureVisible : function(row, col, hscroll)
10209     {
10210         //Roo.log('GridView.ensureVisible,' + row + ',' + col);
10211         //return null; //disable for testing.
10212         if(typeof row != "number"){
10213             row = row.rowIndex;
10214         }
10215         if(row < 0 && row >= this.ds.getCount()){
10216             return  null;
10217         }
10218         col = (col !== undefined ? col : 0);
10219         var cm = this.cm;
10220         while(cm.isHidden(col)){
10221             col++;
10222         }
10223
10224         var el = this.getCellDom(row, col);
10225         if(!el){
10226             return null;
10227         }
10228         var c = this.bodyEl.dom;
10229
10230         var ctop = parseInt(el.offsetTop, 10);
10231         var cleft = parseInt(el.offsetLeft, 10);
10232         var cbot = ctop + el.offsetHeight;
10233         var cright = cleft + el.offsetWidth;
10234
10235         //var ch = c.clientHeight - this.mainHd.dom.offsetHeight;
10236         var ch = 0; //?? header is not withing the area?
10237         var stop = parseInt(c.scrollTop, 10);
10238         var sleft = parseInt(c.scrollLeft, 10);
10239         var sbot = stop + ch;
10240         var sright = sleft + c.clientWidth;
10241         /*
10242         Roo.log('GridView.ensureVisible:' +
10243                 ' ctop:' + ctop +
10244                 ' c.clientHeight:' + c.clientHeight +
10245                 ' this.mainHd.dom.offsetHeight:' + this.mainHd.dom.offsetHeight +
10246                 ' stop:' + stop +
10247                 ' cbot:' + cbot +
10248                 ' sbot:' + sbot +
10249                 ' ch:' + ch  
10250                 );
10251         */
10252         if(ctop < stop){
10253             c.scrollTop = ctop;
10254             //Roo.log("set scrolltop to ctop DISABLE?");
10255         }else if(cbot > sbot){
10256             //Roo.log("set scrolltop to cbot-ch");
10257             c.scrollTop = cbot-ch;
10258         }
10259
10260         if(hscroll !== false){
10261             if(cleft < sleft){
10262                 c.scrollLeft = cleft;
10263             }else if(cright > sright){
10264                 c.scrollLeft = cright-c.clientWidth;
10265             }
10266         }
10267
10268         return el;
10269     },
10270     
10271     
10272     insertRow : function(dm, rowIndex, isUpdate){
10273         
10274         if(!isUpdate){
10275             this.fireEvent("beforerowsinserted", this, rowIndex);
10276         }
10277             //var s = this.getScrollState();
10278         var row = this.renderRow(this.cm, this.store, rowIndex);
10279         // insert before rowIndex..
10280         var e = this.bodyEl.createChild(row,this.getRowDom(rowIndex));
10281         
10282         var _this = this;
10283                 
10284         if(row.cellObjects.length){
10285             Roo.each(row.cellObjects, function(r){
10286                 _this.renderCellObject(r);
10287             })
10288         }
10289             
10290         if(!isUpdate){
10291             this.fireEvent("rowsinserted", this, rowIndex);
10292             //this.syncRowHeights(firstRow, lastRow);
10293             //this.stripeRows(firstRow);
10294             //this.layout();
10295         }
10296         
10297     },
10298     
10299     
10300     getRowDom : function(rowIndex)
10301     {
10302         var rows = this.el.select('tbody > tr', true).elements;
10303         
10304         return (typeof(rows[rowIndex]) == 'undefined') ? false : rows[rowIndex];
10305         
10306     },
10307     getCellDom : function(rowIndex, colIndex)
10308     {
10309         var row = this.getRowDom(rowIndex);
10310         if (row === false) {
10311             return false;
10312         }
10313         var cols = row.select('td', true).elements;
10314         return (typeof(cols[colIndex]) == 'undefined') ? false : cols[colIndex];
10315         
10316     },
10317     
10318     // returns the object tree for a tr..
10319   
10320     
10321     renderRow : function(cm, ds, rowIndex) 
10322     {
10323         var d = ds.getAt(rowIndex);
10324         
10325         var row = {
10326             tag : 'tr',
10327             cls : 'x-row-' + rowIndex,
10328             cn : []
10329         };
10330             
10331         var cellObjects = [];
10332         
10333         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
10334             var config = cm.config[i];
10335             
10336             var renderer = cm.getRenderer(i);
10337             var value = '';
10338             var id = false;
10339             
10340             if(typeof(renderer) !== 'undefined'){
10341                 value = renderer.call(config, d.data[cm.getDataIndex(i)], false, d);
10342             }
10343             // if object are returned, then they are expected to be Roo.bootstrap.Component instances
10344             // and are rendered into the cells after the row is rendered - using the id for the element.
10345             
10346             if(typeof(value) === 'object'){
10347                 id = Roo.id();
10348                 cellObjects.push({
10349                     container : id,
10350                     cfg : value 
10351                 })
10352             }
10353             
10354             var rowcfg = {
10355                 record: d,
10356                 rowIndex : rowIndex,
10357                 colIndex : i,
10358                 rowClass : ''
10359             };
10360
10361             this.fireEvent('rowclass', this, rowcfg);
10362             
10363             var td = {
10364                 tag: 'td',
10365                 // this might end up displaying HTML?
10366                 // this is too messy... - better to only do it on columsn you know are going to be too long
10367                 //tooltip : (typeof(value) === 'object') ? '' : value,
10368                 cls : rowcfg.rowClass + ' x-col-' + i,
10369                 style: '',
10370                 html: (typeof(value) === 'object') ? '' : value
10371             };
10372             
10373             if (id) {
10374                 td.id = id;
10375             }
10376             
10377             if(typeof(config.colspan) != 'undefined'){
10378                 td.colspan = config.colspan;
10379             }
10380             
10381             
10382             
10383             if(typeof(config.align) != 'undefined' && config.align.length){
10384                 td.style += ' text-align:' + config.align + ';';
10385             }
10386             if(typeof(config.valign) != 'undefined' && config.valign.length){
10387                 td.style += ' vertical-align:' + config.valign + ';';
10388             }
10389             /*
10390             if(typeof(config.width) != 'undefined'){
10391                 td.style += ' width:' +  config.width + 'px;';
10392             }
10393             */
10394             
10395             if(typeof(config.cursor) != 'undefined'){
10396                 td.style += ' cursor:' +  config.cursor + ';';
10397             }
10398             
10399             if(typeof(config.cls) != 'undefined'){
10400                 td.cls = (typeof(td.cls) == 'undefined') ? config.cls : (td.cls + ' ' + config.cls);
10401             }
10402             if (this.responsive) {
10403                 ['xs','sm','md','lg'].map(function(size){
10404                     
10405                     if(typeof(config[size]) == 'undefined'){
10406                         return;
10407                     }
10408                     
10409                     
10410                       
10411                     if (!config[size]) { // 0 = hidden
10412                         // BS 4 '0' is treated as hide that column and below.
10413                         td.cls += ' hidden-' + size + ' hidden' + size + '-down';
10414                         return;
10415                     }
10416                     
10417                     td.cls += ' col-' + size + '-' + config[size] + (
10418                         size == 'xs' ? (' col-' +   config[size] ) : '' // bs4 col-{num} replaces col-xs
10419                     );
10420                      
10421     
10422                 });
10423             }
10424             row.cn.push(td);
10425            
10426         }
10427         
10428         row.cellObjects = cellObjects;
10429         
10430         return row;
10431           
10432     },
10433     
10434     
10435     
10436     onBeforeLoad : function()
10437     {
10438         this.el.unmask(); // if needed.
10439     },
10440      /**
10441      * Remove all rows
10442      */
10443     clear : function()
10444     {
10445         this.el.select('tbody', true).first().dom.innerHTML = '';
10446     },
10447     /**
10448      * Show or hide a row.
10449      * @param {Number} rowIndex to show or hide
10450      * @param {Boolean} state hide
10451      */
10452     setRowVisibility : function(rowIndex, state)
10453     {
10454         var bt = this.bodyEl.dom;
10455         
10456         var rows = this.el.select('tbody > tr', true).elements;
10457         
10458         if(typeof(rows[rowIndex]) == 'undefined'){
10459             return;
10460         }
10461         rows[rowIndex][ state ? 'removeClass' : 'addClass']('d-none');
10462         
10463     },
10464     
10465     
10466     getSelectionModel : function(){
10467         if(!this.selModel){
10468             this.selModel = new Roo.bootstrap.Table.RowSelectionModel({grid: this});
10469         }
10470         return this.selModel;
10471     },
10472     /*
10473      * Render the Roo.bootstrap object from renderder
10474      */
10475     renderCellObject : function(r)
10476     {
10477         var _this = this;
10478         
10479         r.cfg.parentId = (typeof(r.container) == 'string') ? r.container : r.container.id;
10480         
10481         var t = r.cfg.render(r.container);
10482         
10483         if(r.cfg.cn){
10484             Roo.each(r.cfg.cn, function(c){
10485                 var child = {
10486                     container: t.getChildContainer(),
10487                     cfg: c
10488                 };
10489                 _this.renderCellObject(child);
10490             })
10491         }
10492     },
10493     /**
10494      * get the Row Index from a dom element.
10495      * @param {Roo.Element} row The row to look for
10496      * @returns {Number} the row
10497      */
10498     getRowIndex : function(row)
10499     {
10500         var rowIndex = -1;
10501         
10502         Roo.each(this.el.select('tbody > tr', true).elements, function(el, index){
10503             if(el != row){
10504                 return;
10505             }
10506             
10507             rowIndex = index;
10508         });
10509         
10510         return rowIndex;
10511     },
10512     /**
10513      * get the header TH element for columnIndex
10514      * @param {Number} columnIndex
10515      * @returns {Roo.Element}
10516      */
10517     getHeaderIndex: function(colIndex)
10518     {
10519         var cols = this.headEl.select('th', true).elements;
10520         return cols[colIndex]; 
10521     },
10522     /**
10523      * get the Column Index from a dom element. (using regex on x-hcol-{colid})
10524      * @param {domElement} cell to look for
10525      * @returns {Number} the column
10526      */
10527     getCellIndex : function(cell)
10528     {
10529         var id = String(cell.className).match(Roo.bootstrap.Table.cellRE);
10530         if(id){
10531             return parseInt(id[1], 10);
10532         }
10533         return 0;
10534     },
10535      /**
10536      * Returns the grid's underlying element = used by panel.Grid
10537      * @return {Element} The element
10538      */
10539     getGridEl : function(){
10540         return this.el;
10541     },
10542      /**
10543      * Forces a resize - used by panel.Grid
10544      * @return {Element} The element
10545      */
10546     autoSize : function()
10547     {
10548         if(this.disableAutoSize) {
10549             return;
10550         }
10551         //var ctr = Roo.get(this.container.dom.parentElement);
10552         var ctr = Roo.get(this.el.dom);
10553         
10554         var thd = this.getGridEl().select('thead',true).first();
10555         var tbd = this.getGridEl().select('tbody', true).first();
10556         var tfd = this.getGridEl().select('tfoot', true).first();
10557         
10558         var cw = ctr.getWidth();
10559         this.getGridEl().select('tfoot tr, tfoot  td',true).setWidth(cw);
10560         
10561         if (tbd) {
10562             
10563             tbd.setWidth(ctr.getWidth());
10564             // if the body has a max height - and then scrolls - we should perhaps set up the height here
10565             // this needs fixing for various usage - currently only hydra job advers I think..
10566             //tdb.setHeight(
10567             //        ctr.getHeight() - ((thd ? thd.getHeight() : 0) + (tfd ? tfd.getHeight() : 0))
10568             //); 
10569             var barsize = (tbd.dom.offsetWidth - tbd.dom.clientWidth);
10570             cw -= barsize;
10571         }
10572         cw = Math.max(cw, this.totalWidth);
10573         this.getGridEl().select('tbody tr',true).setWidth(cw);
10574         this.initCSS();
10575         
10576         // resize 'expandable coloumn?
10577         
10578         return; // we doe not have a view in this design..
10579         
10580     },
10581     onBodyScroll: function()
10582     {
10583         //Roo.log("body scrolled');" + this.bodyEl.dom.scrollLeft);
10584         if(this.headEl){
10585             this.headEl.setStyle({
10586                 'position' : 'relative',
10587                 'left': (-1* this.bodyEl.dom.scrollLeft) + 'px'
10588             });
10589         }
10590         
10591         if(this.lazyLoad){
10592             
10593             var scrollHeight = this.bodyEl.dom.scrollHeight;
10594             
10595             var scrollTop = Math.ceil(this.bodyEl.getScroll().top);
10596             
10597             var height = this.bodyEl.getHeight();
10598             
10599             if(scrollHeight - height == scrollTop) {
10600                 
10601                 var total = this.ds.getTotalCount();
10602                 
10603                 if(this.footer.cursor + this.footer.pageSize < total){
10604                     
10605                     this.footer.ds.load({
10606                         params : {
10607                             start : this.footer.cursor + this.footer.pageSize,
10608                             limit : this.footer.pageSize
10609                         },
10610                         add : true
10611                     });
10612                 }
10613             }
10614             
10615         }
10616     },
10617     onColumnSplitterMoved : function(i, diff)
10618     {
10619         this.userResized = true;
10620         
10621         var cm = this.colModel;
10622         
10623         var w = this.getHeaderIndex(i).getWidth() + diff;
10624         
10625         
10626         cm.setColumnWidth(i, w, true);
10627         this.initCSS();
10628         //var cid = cm.getColumnId(i); << not used in this version?
10629        /* Roo.log(['#' + this.id + ' .x-col-' + i, "width", w + "px"]);
10630         
10631         this.CSS.updateRule( '#' + this.id + ' .x-col-' + i, "width", w + "px");
10632         this.CSS.updateRule('#' + this.id + ' .x-hcol-' + i, "width", w + "px");
10633         this.CSS.updateRule('#' + this.id + ' .x-grid-split-' + i, "left", w + "px");
10634 */
10635         //this.updateSplitters();
10636         //this.layout(); << ??
10637         this.fireEvent("columnresize", i, w);
10638     },
10639     onHeaderChange : function()
10640     {
10641         var header = this.renderHeader();
10642         var table = this.el.select('table', true).first();
10643         
10644         this.headEl.remove();
10645         this.headEl = table.createChild(header, this.bodyEl, false);
10646         
10647         Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
10648             e.on('click', this.sort, this);
10649         }, this);
10650         
10651         if(this.enableColumnResize !== false && Roo.grid.SplitDragZone){
10652             new Roo.grid.SplitDragZone(this, this.headEl.dom, false); // not sure what 'lockedHd is for this implementation..)
10653         }
10654         
10655     },
10656     
10657     onHiddenChange : function(colModel, colIndex, hidden)
10658     {
10659         /*
10660         this.cm.setHidden()
10661         var thSelector = '#' + this.id + ' .x-hcol-' + colIndex;
10662         var tdSelector = '#' + this.id + ' .x-col-' + colIndex;
10663         
10664         this.CSS.updateRule(thSelector, "display", "");
10665         this.CSS.updateRule(tdSelector, "display", "");
10666         
10667         if(hidden){
10668             this.CSS.updateRule(thSelector, "display", "none");
10669             this.CSS.updateRule(tdSelector, "display", "none");
10670         }
10671         */
10672         // onload calls initCSS()
10673         this.onHeaderChange();
10674         this.onLoad();
10675     },
10676     
10677     setColumnWidth: function(col_index, width)
10678     {
10679         // width = "md-2 xs-2..."
10680         if(!this.colModel.config[col_index]) {
10681             return;
10682         }
10683         
10684         var w = width.split(" ");
10685         
10686         var rows = this.el.dom.getElementsByClassName("x-col-"+col_index);
10687         
10688         var h_row = this.el.dom.getElementsByClassName("x-hcol-"+col_index);
10689         
10690         
10691         for(var j = 0; j < w.length; j++) {
10692             
10693             if(!w[j]) {
10694                 continue;
10695             }
10696             
10697             var size_cls = w[j].split("-");
10698             
10699             if(!Number.isInteger(size_cls[1] * 1)) {
10700                 continue;
10701             }
10702             
10703             if(!this.colModel.config[col_index][size_cls[0]]) {
10704                 continue;
10705             }
10706             
10707             if(!h_row[0].classList.contains("col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]])) {
10708                 continue;
10709             }
10710             
10711             h_row[0].classList.replace(
10712                 "col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]],
10713                 "col-"+size_cls[0]+"-"+size_cls[1]
10714             );
10715             
10716             for(var i = 0; i < rows.length; i++) {
10717                 
10718                 var size_cls = w[j].split("-");
10719                 
10720                 if(!Number.isInteger(size_cls[1] * 1)) {
10721                     continue;
10722                 }
10723                 
10724                 if(!this.colModel.config[col_index][size_cls[0]]) {
10725                     continue;
10726                 }
10727                 
10728                 if(!rows[i].classList.contains("col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]])) {
10729                     continue;
10730                 }
10731                 
10732                 rows[i].classList.replace(
10733                     "col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]],
10734                     "col-"+size_cls[0]+"-"+size_cls[1]
10735                 );
10736             }
10737             
10738             this.colModel.config[col_index][size_cls[0]] = size_cls[1];
10739         }
10740     }
10741 });
10742
10743 // currently only used to find the split on drag.. 
10744 Roo.bootstrap.Table.cellRE = /(?:.*?)x-grid-(?:hd|cell|split)-([\d]+)(?:.*?)/;
10745
10746 /**
10747  * @depricated
10748 */
10749 Roo.bootstrap.Table.AbstractSelectionModel = Roo.grid.AbstractSelectionModel;
10750 Roo.bootstrap.Table.RowSelectionModel = Roo.grid.RowSelectionModel;
10751 /*
10752  * - LGPL
10753  *
10754  * table cell
10755  * 
10756  */
10757
10758 /**
10759  * @class Roo.bootstrap.TableCell
10760  * @extends Roo.bootstrap.Component
10761  * @children Roo.bootstrap.Component
10762  * @parent Roo.bootstrap.TableRow
10763  * Bootstrap TableCell class
10764  * 
10765  * @cfg {String} html cell contain text
10766  * @cfg {String} cls cell class
10767  * @cfg {String} tag cell tag (td|th) default td
10768  * @cfg {String} abbr Specifies an abbreviated version of the content in a cell
10769  * @cfg {String} align Aligns the content in a cell
10770  * @cfg {String} axis Categorizes cells
10771  * @cfg {String} bgcolor Specifies the background color of a cell
10772  * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
10773  * @cfg {Number} colspan Specifies the number of columns a cell should span
10774  * @cfg {String} headers Specifies one or more header cells a cell is related to
10775  * @cfg {Number} height Sets the height of a cell
10776  * @cfg {String} nowrap Specifies that the content inside a cell should not wrap
10777  * @cfg {Number} rowspan Sets the number of rows a cell should span
10778  * @cfg {String} scope Defines a way to associate header cells and data cells in a table
10779  * @cfg {String} valign Vertical aligns the content in a cell
10780  * @cfg {Number} width Specifies the width of a cell
10781  * 
10782  * @constructor
10783  * Create a new TableCell
10784  * @param {Object} config The config object
10785  */
10786
10787 Roo.bootstrap.TableCell = function(config){
10788     Roo.bootstrap.TableCell.superclass.constructor.call(this, config);
10789 };
10790
10791 Roo.extend(Roo.bootstrap.TableCell, Roo.bootstrap.Component,  {
10792     
10793     html: false,
10794     cls: false,
10795     tag: false,
10796     abbr: false,
10797     align: false,
10798     axis: false,
10799     bgcolor: false,
10800     charoff: false,
10801     colspan: false,
10802     headers: false,
10803     height: false,
10804     nowrap: false,
10805     rowspan: false,
10806     scope: false,
10807     valign: false,
10808     width: false,
10809     
10810     
10811     getAutoCreate : function(){
10812         var cfg = Roo.apply({}, Roo.bootstrap.TableCell.superclass.getAutoCreate.call(this));
10813         
10814         cfg = {
10815             tag: 'td'
10816         };
10817         
10818         if(this.tag){
10819             cfg.tag = this.tag;
10820         }
10821         
10822         if (this.html) {
10823             cfg.html=this.html
10824         }
10825         if (this.cls) {
10826             cfg.cls=this.cls
10827         }
10828         if (this.abbr) {
10829             cfg.abbr=this.abbr
10830         }
10831         if (this.align) {
10832             cfg.align=this.align
10833         }
10834         if (this.axis) {
10835             cfg.axis=this.axis
10836         }
10837         if (this.bgcolor) {
10838             cfg.bgcolor=this.bgcolor
10839         }
10840         if (this.charoff) {
10841             cfg.charoff=this.charoff
10842         }
10843         if (this.colspan) {
10844             cfg.colspan=this.colspan
10845         }
10846         if (this.headers) {
10847             cfg.headers=this.headers
10848         }
10849         if (this.height) {
10850             cfg.height=this.height
10851         }
10852         if (this.nowrap) {
10853             cfg.nowrap=this.nowrap
10854         }
10855         if (this.rowspan) {
10856             cfg.rowspan=this.rowspan
10857         }
10858         if (this.scope) {
10859             cfg.scope=this.scope
10860         }
10861         if (this.valign) {
10862             cfg.valign=this.valign
10863         }
10864         if (this.width) {
10865             cfg.width=this.width
10866         }
10867         
10868         
10869         return cfg;
10870     }
10871    
10872 });
10873
10874  
10875
10876  /*
10877  * - LGPL
10878  *
10879  * table row
10880  * 
10881  */
10882
10883 /**
10884  * @class Roo.bootstrap.TableRow
10885  * @extends Roo.bootstrap.Component
10886  * @children Roo.bootstrap.TableCell
10887  * @parent Roo.bootstrap.TableBody
10888  * Bootstrap TableRow class
10889  * @cfg {String} cls row class
10890  * @cfg {String} align Aligns the content in a table row
10891  * @cfg {String} bgcolor Specifies a background color for a table row
10892  * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
10893  * @cfg {String} valign Vertical aligns the content in a table row
10894  * 
10895  * @constructor
10896  * Create a new TableRow
10897  * @param {Object} config The config object
10898  */
10899
10900 Roo.bootstrap.TableRow = function(config){
10901     Roo.bootstrap.TableRow.superclass.constructor.call(this, config);
10902 };
10903
10904 Roo.extend(Roo.bootstrap.TableRow, Roo.bootstrap.Component,  {
10905     
10906     cls: false,
10907     align: false,
10908     bgcolor: false,
10909     charoff: false,
10910     valign: false,
10911     
10912     getAutoCreate : function(){
10913         var cfg = Roo.apply({}, Roo.bootstrap.TableRow.superclass.getAutoCreate.call(this));
10914         
10915         cfg = {
10916             tag: 'tr'
10917         };
10918             
10919         if(this.cls){
10920             cfg.cls = this.cls;
10921         }
10922         if(this.align){
10923             cfg.align = this.align;
10924         }
10925         if(this.bgcolor){
10926             cfg.bgcolor = this.bgcolor;
10927         }
10928         if(this.charoff){
10929             cfg.charoff = this.charoff;
10930         }
10931         if(this.valign){
10932             cfg.valign = this.valign;
10933         }
10934         
10935         return cfg;
10936     }
10937    
10938 });
10939
10940  
10941
10942  /*
10943  * - LGPL
10944  *
10945  * table body
10946  * 
10947  */
10948
10949 /**
10950  * @class Roo.bootstrap.TableBody
10951  * @extends Roo.bootstrap.Component
10952  * @children Roo.bootstrap.TableRow
10953  * @parent Roo.bootstrap.Table
10954  * Bootstrap TableBody class
10955  * @cfg {String} cls element class
10956  * @cfg {String} tag element tag (thead|tbody|tfoot) default tbody
10957  * @cfg {String} align Aligns the content inside the element
10958  * @cfg {Number} charoff Sets the number of characters the content inside the element will be aligned from the character specified by the char attribute
10959  * @cfg {String} valign Vertical aligns the content inside the <tbody> element
10960  * 
10961  * @constructor
10962  * Create a new TableBody
10963  * @param {Object} config The config object
10964  */
10965
10966 Roo.bootstrap.TableBody = function(config){
10967     Roo.bootstrap.TableBody.superclass.constructor.call(this, config);
10968 };
10969
10970 Roo.extend(Roo.bootstrap.TableBody, Roo.bootstrap.Component,  {
10971     
10972     cls: false,
10973     tag: false,
10974     align: false,
10975     charoff: false,
10976     valign: false,
10977     
10978     getAutoCreate : function(){
10979         var cfg = Roo.apply({}, Roo.bootstrap.TableBody.superclass.getAutoCreate.call(this));
10980         
10981         cfg = {
10982             tag: 'tbody'
10983         };
10984             
10985         if (this.cls) {
10986             cfg.cls=this.cls
10987         }
10988         if(this.tag){
10989             cfg.tag = this.tag;
10990         }
10991         
10992         if(this.align){
10993             cfg.align = this.align;
10994         }
10995         if(this.charoff){
10996             cfg.charoff = this.charoff;
10997         }
10998         if(this.valign){
10999             cfg.valign = this.valign;
11000         }
11001         
11002         return cfg;
11003     }
11004     
11005     
11006 //    initEvents : function()
11007 //    {
11008 //        
11009 //        if(!this.store){
11010 //            return;
11011 //        }
11012 //        
11013 //        this.store = Roo.factory(this.store, Roo.data);
11014 //        this.store.on('load', this.onLoad, this);
11015 //        
11016 //        this.store.load();
11017 //        
11018 //    },
11019 //    
11020 //    onLoad: function () 
11021 //    {   
11022 //        this.fireEvent('load', this);
11023 //    }
11024 //    
11025 //   
11026 });
11027
11028  
11029
11030  /*
11031  * Based on:
11032  * Ext JS Library 1.1.1
11033  * Copyright(c) 2006-2007, Ext JS, LLC.
11034  *
11035  * Originally Released Under LGPL - original licence link has changed is not relivant.
11036  *
11037  * Fork - LGPL
11038  * <script type="text/javascript">
11039  */
11040
11041 // as we use this in bootstrap.
11042 Roo.namespace('Roo.form');
11043  /**
11044  * @class Roo.form.Action
11045  * Internal Class used to handle form actions
11046  * @constructor
11047  * @param {Roo.form.BasicForm} el The form element or its id
11048  * @param {Object} config Configuration options
11049  */
11050
11051  
11052  
11053 // define the action interface
11054 Roo.form.Action = function(form, options){
11055     this.form = form;
11056     this.options = options || {};
11057 };
11058 /**
11059  * Client Validation Failed
11060  * @const 
11061  */
11062 Roo.form.Action.CLIENT_INVALID = 'client';
11063 /**
11064  * Server Validation Failed
11065  * @const 
11066  */
11067 Roo.form.Action.SERVER_INVALID = 'server';
11068  /**
11069  * Connect to Server Failed
11070  * @const 
11071  */
11072 Roo.form.Action.CONNECT_FAILURE = 'connect';
11073 /**
11074  * Reading Data from Server Failed
11075  * @const 
11076  */
11077 Roo.form.Action.LOAD_FAILURE = 'load';
11078
11079 Roo.form.Action.prototype = {
11080     type : 'default',
11081     failureType : undefined,
11082     response : undefined,
11083     result : undefined,
11084
11085     // interface method
11086     run : function(options){
11087
11088     },
11089
11090     // interface method
11091     success : function(response){
11092
11093     },
11094
11095     // interface method
11096     handleResponse : function(response){
11097
11098     },
11099
11100     // default connection failure
11101     failure : function(response){
11102         
11103         this.response = response;
11104         this.failureType = Roo.form.Action.CONNECT_FAILURE;
11105         this.form.afterAction(this, false);
11106     },
11107
11108     processResponse : function(response){
11109         this.response = response;
11110         if(!response.responseText){
11111             return true;
11112         }
11113         this.result = this.handleResponse(response);
11114         return this.result;
11115     },
11116
11117     // utility functions used internally
11118     getUrl : function(appendParams){
11119         var url = this.options.url || this.form.url || this.form.el.dom.action;
11120         if(appendParams){
11121             var p = this.getParams();
11122             if(p){
11123                 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
11124             }
11125         }
11126         return url;
11127     },
11128
11129     getMethod : function(){
11130         return (this.options.method || this.form.method || this.form.el.dom.method || 'POST').toUpperCase();
11131     },
11132
11133     getParams : function(){
11134         var bp = this.form.baseParams;
11135         var p = this.options.params;
11136         if(p){
11137             if(typeof p == "object"){
11138                 p = Roo.urlEncode(Roo.applyIf(p, bp));
11139             }else if(typeof p == 'string' && bp){
11140                 p += '&' + Roo.urlEncode(bp);
11141             }
11142         }else if(bp){
11143             p = Roo.urlEncode(bp);
11144         }
11145         return p;
11146     },
11147
11148     createCallback : function(){
11149         return {
11150             success: this.success,
11151             failure: this.failure,
11152             scope: this,
11153             timeout: (this.form.timeout*1000),
11154             upload: this.form.fileUpload ? this.success : undefined
11155         };
11156     }
11157 };
11158
11159 Roo.form.Action.Submit = function(form, options){
11160     Roo.form.Action.Submit.superclass.constructor.call(this, form, options);
11161 };
11162
11163 Roo.extend(Roo.form.Action.Submit, Roo.form.Action, {
11164     type : 'submit',
11165
11166     haveProgress : false,
11167     uploadComplete : false,
11168     
11169     // uploadProgress indicator.
11170     uploadProgress : function()
11171     {
11172         if (!this.form.progressUrl) {
11173             return;
11174         }
11175         
11176         if (!this.haveProgress) {
11177             Roo.MessageBox.progress("Uploading", "Uploading");
11178         }
11179         if (this.uploadComplete) {
11180            Roo.MessageBox.hide();
11181            return;
11182         }
11183         
11184         this.haveProgress = true;
11185    
11186         var uid = this.form.findField('UPLOAD_IDENTIFIER').getValue();
11187         
11188         var c = new Roo.data.Connection();
11189         c.request({
11190             url : this.form.progressUrl,
11191             params: {
11192                 id : uid
11193             },
11194             method: 'GET',
11195             success : function(req){
11196                //console.log(data);
11197                 var rdata = false;
11198                 var edata;
11199                 try  {
11200                    rdata = Roo.decode(req.responseText)
11201                 } catch (e) {
11202                     Roo.log("Invalid data from server..");
11203                     Roo.log(edata);
11204                     return;
11205                 }
11206                 if (!rdata || !rdata.success) {
11207                     Roo.log(rdata);
11208                     Roo.MessageBox.alert(Roo.encode(rdata));
11209                     return;
11210                 }
11211                 var data = rdata.data;
11212                 
11213                 if (this.uploadComplete) {
11214                    Roo.MessageBox.hide();
11215                    return;
11216                 }
11217                    
11218                 if (data){
11219                     Roo.MessageBox.updateProgress(data.bytes_uploaded/data.bytes_total,
11220                        Math.floor((data.bytes_total - data.bytes_uploaded)/1000) + 'k remaining'
11221                     );
11222                 }
11223                 this.uploadProgress.defer(2000,this);
11224             },
11225        
11226             failure: function(data) {
11227                 Roo.log('progress url failed ');
11228                 Roo.log(data);
11229             },
11230             scope : this
11231         });
11232            
11233     },
11234     
11235     
11236     run : function()
11237     {
11238         // run get Values on the form, so it syncs any secondary forms.
11239         this.form.getValues();
11240         
11241         var o = this.options;
11242         var method = this.getMethod();
11243         var isPost = method == 'POST';
11244         if(o.clientValidation === false || this.form.isValid()){
11245             
11246             if (this.form.progressUrl) {
11247                 this.form.findField('UPLOAD_IDENTIFIER').setValue(
11248                     (new Date() * 1) + '' + Math.random());
11249                     
11250             } 
11251             
11252             
11253             Roo.Ajax.request(Roo.apply(this.createCallback(), {
11254                 form:this.form.el.dom,
11255                 url:this.getUrl(!isPost),
11256                 method: method,
11257                 params:isPost ? this.getParams() : null,
11258                 isUpload: this.form.fileUpload,
11259                 formData : this.form.formData
11260             }));
11261             
11262             this.uploadProgress();
11263
11264         }else if (o.clientValidation !== false){ // client validation failed
11265             this.failureType = Roo.form.Action.CLIENT_INVALID;
11266             this.form.afterAction(this, false);
11267         }
11268     },
11269
11270     success : function(response)
11271     {
11272         this.uploadComplete= true;
11273         if (this.haveProgress) {
11274             Roo.MessageBox.hide();
11275         }
11276         
11277         
11278         var result = this.processResponse(response);
11279         if(result === true || result.success){
11280             this.form.afterAction(this, true);
11281             return;
11282         }
11283         if(result.errors){
11284             this.form.markInvalid(result.errors);
11285             this.failureType = Roo.form.Action.SERVER_INVALID;
11286         }
11287         this.form.afterAction(this, false);
11288     },
11289     failure : function(response)
11290     {
11291         this.uploadComplete= true;
11292         if (this.haveProgress) {
11293             Roo.MessageBox.hide();
11294         }
11295         
11296         this.response = response;
11297         this.failureType = Roo.form.Action.CONNECT_FAILURE;
11298         this.form.afterAction(this, false);
11299     },
11300     
11301     handleResponse : function(response){
11302         if(this.form.errorReader){
11303             var rs = this.form.errorReader.read(response);
11304             var errors = [];
11305             if(rs.records){
11306                 for(var i = 0, len = rs.records.length; i < len; i++) {
11307                     var r = rs.records[i];
11308                     errors[i] = r.data;
11309                 }
11310             }
11311             if(errors.length < 1){
11312                 errors = null;
11313             }
11314             return {
11315                 success : rs.success,
11316                 errors : errors
11317             };
11318         }
11319         var ret = false;
11320         try {
11321             var rt = response.responseText;
11322             if (rt.match(/^\<!--\[CDATA\[/)) {
11323                 rt = rt.replace(/^\<!--\[CDATA\[/,'');
11324                 rt = rt.replace(/\]\]--\>$/,'');
11325             }
11326             
11327             ret = Roo.decode(rt);
11328         } catch (e) {
11329             ret = {
11330                 success: false,
11331                 errorMsg: "Failed to read server message: " + (response ? response.responseText : ' - no message'),
11332                 errors : []
11333             };
11334         }
11335         return ret;
11336         
11337     }
11338 });
11339
11340
11341 Roo.form.Action.Load = function(form, options){
11342     Roo.form.Action.Load.superclass.constructor.call(this, form, options);
11343     this.reader = this.form.reader;
11344 };
11345
11346 Roo.extend(Roo.form.Action.Load, Roo.form.Action, {
11347     type : 'load',
11348
11349     run : function(){
11350         
11351         Roo.Ajax.request(Roo.apply(
11352                 this.createCallback(), {
11353                     method:this.getMethod(),
11354                     url:this.getUrl(false),
11355                     params:this.getParams()
11356         }));
11357     },
11358
11359     success : function(response){
11360         
11361         var result = this.processResponse(response);
11362         if(result === true || !result.success || !result.data){
11363             this.failureType = Roo.form.Action.LOAD_FAILURE;
11364             this.form.afterAction(this, false);
11365             return;
11366         }
11367         this.form.clearInvalid();
11368         this.form.setValues(result.data);
11369         this.form.afterAction(this, true);
11370     },
11371
11372     handleResponse : function(response){
11373         if(this.form.reader){
11374             var rs = this.form.reader.read(response);
11375             var data = rs.records && rs.records[0] ? rs.records[0].data : null;
11376             return {
11377                 success : rs.success,
11378                 data : data
11379             };
11380         }
11381         return Roo.decode(response.responseText);
11382     }
11383 });
11384
11385 Roo.form.Action.ACTION_TYPES = {
11386     'load' : Roo.form.Action.Load,
11387     'submit' : Roo.form.Action.Submit
11388 };/*
11389  * - LGPL
11390  *
11391  * form
11392  *
11393  */
11394
11395 /**
11396  * @class Roo.bootstrap.form.Form
11397  * @extends Roo.bootstrap.Component
11398  * @children Roo.bootstrap.Component
11399  * Bootstrap Form class
11400  * @cfg {String} method  GET | POST (default POST)
11401  * @cfg {String} labelAlign top | left (default top)
11402  * @cfg {String} align left  | right - for navbars
11403  * @cfg {Boolean} loadMask load mask when submit (default true)
11404
11405  *
11406  * @constructor
11407  * Create a new Form
11408  * @param {Object} config The config object
11409  */
11410
11411
11412 Roo.bootstrap.form.Form = function(config){
11413     
11414     Roo.bootstrap.form.Form.superclass.constructor.call(this, config);
11415     
11416     Roo.bootstrap.form.Form.popover.apply();
11417     
11418     this.addEvents({
11419         /**
11420          * @event clientvalidation
11421          * If the monitorValid config option is true, this event fires repetitively to notify of valid state
11422          * @param {Form} this
11423          * @param {Boolean} valid true if the form has passed client-side validation
11424          */
11425         clientvalidation: true,
11426         /**
11427          * @event beforeaction
11428          * Fires before any action is performed. Return false to cancel the action.
11429          * @param {Form} this
11430          * @param {Action} action The action to be performed
11431          */
11432         beforeaction: true,
11433         /**
11434          * @event actionfailed
11435          * Fires when an action fails.
11436          * @param {Form} this
11437          * @param {Action} action The action that failed
11438          */
11439         actionfailed : true,
11440         /**
11441          * @event actioncomplete
11442          * Fires when an action is completed.
11443          * @param {Form} this
11444          * @param {Action} action The action that completed
11445          */
11446         actioncomplete : true
11447     });
11448 };
11449
11450 Roo.extend(Roo.bootstrap.form.Form, Roo.bootstrap.Component,  {
11451
11452      /**
11453      * @cfg {String} method
11454      * The request method to use (GET or POST) for form actions if one isn't supplied in the action options.
11455      */
11456     method : 'POST',
11457     /**
11458      * @cfg {String} url
11459      * The URL to use for form actions if one isn't supplied in the action options.
11460      */
11461     /**
11462      * @cfg {Boolean} fileUpload
11463      * Set to true if this form is a file upload.
11464      */
11465
11466     /**
11467      * @cfg {Object} baseParams
11468      * Parameters to pass with all requests. e.g. baseParams: {id: '123', foo: 'bar'}.
11469      */
11470
11471     /**
11472      * @cfg {Number} timeout Timeout for form actions in seconds (default is 30 seconds).
11473      */
11474     timeout: 30,
11475     /**
11476      * @cfg {Sting} align (left|right) for navbar forms
11477      */
11478     align : 'left',
11479
11480     // private
11481     activeAction : null,
11482
11483     /**
11484      * By default wait messages are displayed with Roo.MessageBox.wait. You can target a specific
11485      * element by passing it or its id or mask the form itself by passing in true.
11486      * @type Mixed
11487      */
11488     waitMsgTarget : false,
11489
11490     loadMask : true,
11491     
11492     /**
11493      * @cfg {Boolean} errorMask (true|false) default false
11494      */
11495     errorMask : false,
11496     
11497     /**
11498      * @cfg {Number} maskOffset Default 100
11499      */
11500     maskOffset : 100,
11501     
11502     /**
11503      * @cfg {Boolean} maskBody
11504      */
11505     maskBody : false,
11506
11507     getAutoCreate : function(){
11508
11509         var cfg = {
11510             tag: 'form',
11511             method : this.method || 'POST',
11512             id : this.id || Roo.id(),
11513             cls : ''
11514         };
11515         if (this.parent().xtype.match(/^Nav/)) {
11516             cfg.cls = 'navbar-form form-inline navbar-' + this.align;
11517
11518         }
11519
11520         if (this.labelAlign == 'left' ) {
11521             cfg.cls += ' form-horizontal';
11522         }
11523
11524
11525         return cfg;
11526     },
11527     initEvents : function()
11528     {
11529         this.el.on('submit', this.onSubmit, this);
11530         // this was added as random key presses on the form where triggering form submit.
11531         this.el.on('keypress', function(e) {
11532             if (e.getCharCode() != 13) {
11533                 return true;
11534             }
11535             // we might need to allow it for textareas.. and some other items.
11536             // check e.getTarget().
11537
11538             if(e.getTarget().nodeName.toLowerCase() === 'textarea'){
11539                 return true;
11540             }
11541
11542             Roo.log("keypress blocked");
11543
11544             e.preventDefault();
11545             return false;
11546         });
11547         
11548     },
11549     // private
11550     onSubmit : function(e){
11551         e.stopEvent();
11552     },
11553
11554      /**
11555      * Returns true if client-side validation on the form is successful.
11556      * @return Boolean
11557      */
11558     isValid : function(){
11559         var items = this.getItems();
11560         var valid = true;
11561         var target = false;
11562         
11563         items.each(function(f){
11564             
11565             if(f.validate()){
11566                 return;
11567             }
11568             
11569             Roo.log('invalid field: ' + f.name);
11570             
11571             valid = false;
11572
11573             if(!target && f.el.isVisible(true)){
11574                 target = f;
11575             }
11576            
11577         });
11578         
11579         if(this.errorMask && !valid){
11580             Roo.bootstrap.form.Form.popover.mask(this, target);
11581         }
11582         
11583         return valid;
11584     },
11585     
11586     /**
11587      * Returns true if any fields in this form have changed since their original load.
11588      * @return Boolean
11589      */
11590     isDirty : function(){
11591         var dirty = false;
11592         var items = this.getItems();
11593         items.each(function(f){
11594            if(f.isDirty()){
11595                dirty = true;
11596                return false;
11597            }
11598            return true;
11599         });
11600         return dirty;
11601     },
11602      /**
11603      * Performs a predefined action (submit or load) or custom actions you define on this form.
11604      * @param {String} actionName The name of the action type
11605      * @param {Object} options (optional) The options to pass to the action.  All of the config options listed
11606      * below are supported by both the submit and load actions unless otherwise noted (custom actions could also
11607      * accept other config options):
11608      * <pre>
11609 Property          Type             Description
11610 ----------------  ---------------  ----------------------------------------------------------------------------------
11611 url               String           The url for the action (defaults to the form's url)
11612 method            String           The form method to use (defaults to the form's method, or POST if not defined)
11613 params            String/Object    The params to pass (defaults to the form's baseParams, or none if not defined)
11614 clientValidation  Boolean          Applies to submit only.  Pass true to call form.isValid() prior to posting to
11615                                    validate the form on the client (defaults to false)
11616      * </pre>
11617      * @return {BasicForm} this
11618      */
11619     doAction : function(action, options){
11620         if(typeof action == 'string'){
11621             action = new Roo.form.Action.ACTION_TYPES[action](this, options);
11622         }
11623         if(this.fireEvent('beforeaction', this, action) !== false){
11624             this.beforeAction(action);
11625             action.run.defer(100, action);
11626         }
11627         return this;
11628     },
11629
11630     // private
11631     beforeAction : function(action){
11632         var o = action.options;
11633         
11634         if(this.loadMask){
11635             
11636             if(this.maskBody){
11637                 Roo.get(document.body).mask(o.waitMsg || "Sending", 'x-mask-loading')
11638             } else {
11639                 this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
11640             }
11641         }
11642         // not really supported yet.. ??
11643
11644         //if(this.waitMsgTarget === true){
11645         //  this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
11646         //}else if(this.waitMsgTarget){
11647         //    this.waitMsgTarget = Roo.get(this.waitMsgTarget);
11648         //    this.waitMsgTarget.mask(o.waitMsg || "Sending", 'x-mask-loading');
11649         //}else {
11650         //    Roo.MessageBox.wait(o.waitMsg || "Sending", o.waitTitle || this.waitTitle || 'Please Wait...');
11651        // }
11652
11653     },
11654
11655     // private
11656     afterAction : function(action, success){
11657         this.activeAction = null;
11658         var o = action.options;
11659
11660         if(this.loadMask){
11661             
11662             if(this.maskBody){
11663                 Roo.get(document.body).unmask();
11664             } else {
11665                 this.el.unmask();
11666             }
11667         }
11668         
11669         //if(this.waitMsgTarget === true){
11670 //            this.el.unmask();
11671         //}else if(this.waitMsgTarget){
11672         //    this.waitMsgTarget.unmask();
11673         //}else{
11674         //    Roo.MessageBox.updateProgress(1);
11675         //    Roo.MessageBox.hide();
11676        // }
11677         //
11678         if(success){
11679             if(o.reset){
11680                 this.reset();
11681             }
11682             Roo.callback(o.success, o.scope, [this, action]);
11683             this.fireEvent('actioncomplete', this, action);
11684
11685         }else{
11686
11687             // failure condition..
11688             // we have a scenario where updates need confirming.
11689             // eg. if a locking scenario exists..
11690             // we look for { errors : { needs_confirm : true }} in the response.
11691             if (
11692                 (typeof(action.result) != 'undefined')  &&
11693                 (typeof(action.result.errors) != 'undefined')  &&
11694                 (typeof(action.result.errors.needs_confirm) != 'undefined')
11695            ){
11696                 var _t = this;
11697                 Roo.log("not supported yet");
11698                  /*
11699
11700                 Roo.MessageBox.confirm(
11701                     "Change requires confirmation",
11702                     action.result.errorMsg,
11703                     function(r) {
11704                         if (r != 'yes') {
11705                             return;
11706                         }
11707                         _t.doAction('submit', { params :  { _submit_confirmed : 1 } }  );
11708                     }
11709
11710                 );
11711                 */
11712
11713
11714                 return;
11715             }
11716
11717             Roo.callback(o.failure, o.scope, [this, action]);
11718             // show an error message if no failed handler is set..
11719             if (!this.hasListener('actionfailed')) {
11720                 Roo.log("need to add dialog support");
11721                 /*
11722                 Roo.MessageBox.alert("Error",
11723                     (typeof(action.result) != 'undefined' && typeof(action.result.errorMsg) != 'undefined') ?
11724                         action.result.errorMsg :
11725                         "Saving Failed, please check your entries or try again"
11726                 );
11727                 */
11728             }
11729
11730             this.fireEvent('actionfailed', this, action);
11731         }
11732
11733     },
11734     /**
11735      * Find a Roo.form.Field in this form by id, dataIndex, name or hiddenName
11736      * @param {String} id The value to search for
11737      * @return Field
11738      */
11739     findField : function(id){
11740         var items = this.getItems();
11741         var field = items.get(id);
11742         if(!field){
11743              items.each(function(f){
11744                 if(f.isFormField && (f.dataIndex == id || f.id == id || f.getName() == id)){
11745                     field = f;
11746                     return false;
11747                 }
11748                 return true;
11749             });
11750         }
11751         return field || null;
11752     },
11753      /**
11754      * Mark fields in this form invalid in bulk.
11755      * @param {Array/Object} errors Either an array in the form [{id:'fieldId', msg:'The message'},...] or an object hash of {id: msg, id2: msg2}
11756      * @return {BasicForm} this
11757      */
11758     markInvalid : function(errors){
11759         if(errors instanceof Array){
11760             for(var i = 0, len = errors.length; i < len; i++){
11761                 var fieldError = errors[i];
11762                 var f = this.findField(fieldError.id);
11763                 if(f){
11764                     f.markInvalid(fieldError.msg);
11765                 }
11766             }
11767         }else{
11768             var field, id;
11769             for(id in errors){
11770                 if(typeof errors[id] != 'function' && (field = this.findField(id))){
11771                     field.markInvalid(errors[id]);
11772                 }
11773             }
11774         }
11775         //Roo.each(this.childForms || [], function (f) {
11776         //    f.markInvalid(errors);
11777         //});
11778
11779         return this;
11780     },
11781
11782     /**
11783      * Set values for fields in this form in bulk.
11784      * @param {Array/Object} values Either an array in the form [{id:'fieldId', value:'foo'},...] or an object hash of {id: value, id2: value2}
11785      * @return {BasicForm} this
11786      */
11787     setValues : function(values){
11788         if(values instanceof Array){ // array of objects
11789             for(var i = 0, len = values.length; i < len; i++){
11790                 var v = values[i];
11791                 var f = this.findField(v.id);
11792                 if(f){
11793                     f.setValue(v.value);
11794                     if(this.trackResetOnLoad){
11795                         f.originalValue = f.getValue();
11796                     }
11797                 }
11798             }
11799         }else{ // object hash
11800             var field, id;
11801             for(id in values){
11802                 if(typeof values[id] != 'function' && (field = this.findField(id))){
11803
11804                     if (field.setFromData &&
11805                         field.valueField &&
11806                         field.displayField &&
11807                         // combos' with local stores can
11808                         // be queried via setValue()
11809                         // to set their value..
11810                         (field.store && !field.store.isLocal)
11811                         ) {
11812                         // it's a combo
11813                         var sd = { };
11814                         sd[field.valueField] = typeof(values[field.hiddenName]) == 'undefined' ? '' : values[field.hiddenName];
11815                         sd[field.displayField] = typeof(values[field.name]) == 'undefined' ? '' : values[field.name];
11816                         field.setFromData(sd);
11817
11818                     } else if(field.setFromData && (field.store && !field.store.isLocal)) {
11819                         
11820                         field.setFromData(values);
11821                         
11822                     } else {
11823                         field.setValue(values[id]);
11824                     }
11825
11826
11827                     if(this.trackResetOnLoad){
11828                         field.originalValue = field.getValue();
11829                     }
11830                 }
11831             }
11832         }
11833
11834         //Roo.each(this.childForms || [], function (f) {
11835         //    f.setValues(values);
11836         //});
11837
11838         return this;
11839     },
11840
11841     /**
11842      * Returns the fields in this form as an object with key/value pairs. If multiple fields exist with the same name
11843      * they are returned as an array.
11844      * @param {Boolean} asString
11845      * @return {Object}
11846      */
11847     getValues : function(asString){
11848         //if (this.childForms) {
11849             // copy values from the child forms
11850         //    Roo.each(this.childForms, function (f) {
11851         //        this.setValues(f.getValues());
11852         //    }, this);
11853         //}
11854
11855
11856
11857         var fs = Roo.lib.Ajax.serializeForm(this.el.dom);
11858         if(asString === true){
11859             return fs;
11860         }
11861         return Roo.urlDecode(fs);
11862     },
11863
11864     /**
11865      * Returns the fields in this form as an object with key/value pairs.
11866      * This differs from getValues as it calls getValue on each child item, rather than using dom data.
11867      * @return {Object}
11868      */
11869     getFieldValues : function(with_hidden)
11870     {
11871         var items = this.getItems();
11872         var ret = {};
11873         items.each(function(f){
11874             
11875             if (!f.getName()) {
11876                 return;
11877             }
11878             
11879             var v = f.getValue();
11880             
11881             if (f.inputType =='radio') {
11882                 if (typeof(ret[f.getName()]) == 'undefined') {
11883                     ret[f.getName()] = ''; // empty..
11884                 }
11885
11886                 if (!f.el.dom.checked) {
11887                     return;
11888
11889                 }
11890                 v = f.el.dom.value;
11891
11892             }
11893             
11894             if(f.xtype == 'MoneyField'){
11895                 ret[f.currencyName] = f.getCurrency();
11896             }
11897
11898             // not sure if this supported any more..
11899             if ((typeof(v) == 'object') && f.getRawValue) {
11900                 v = f.getRawValue() ; // dates..
11901             }
11902             // combo boxes where name != hiddenName...
11903             if (f.name !== false && f.name != '' && f.name != f.getName()) {
11904                 ret[f.name] = f.getRawValue();
11905             }
11906             ret[f.getName()] = v;
11907         });
11908
11909         return ret;
11910     },
11911
11912     /**
11913      * Clears all invalid messages in this form.
11914      * @return {BasicForm} this
11915      */
11916     clearInvalid : function(){
11917         var items = this.getItems();
11918
11919         items.each(function(f){
11920            f.clearInvalid();
11921         });
11922
11923         return this;
11924     },
11925
11926     /**
11927      * Resets this form.
11928      * @return {BasicForm} this
11929      */
11930     reset : function(){
11931         var items = this.getItems();
11932         items.each(function(f){
11933             f.reset();
11934         });
11935
11936         Roo.each(this.childForms || [], function (f) {
11937             f.reset();
11938         });
11939
11940
11941         return this;
11942     },
11943     
11944     getItems : function()
11945     {
11946         var r=new Roo.util.MixedCollection(false, function(o){
11947             return o.id || (o.id = Roo.id());
11948         });
11949         var iter = function(el) {
11950             if (el.inputEl) {
11951                 r.add(el);
11952             }
11953             if (!el.items) {
11954                 return;
11955             }
11956             Roo.each(el.items,function(e) {
11957                 iter(e);
11958             });
11959         };
11960
11961         iter(this);
11962         return r;
11963     },
11964     
11965     hideFields : function(items)
11966     {
11967         Roo.each(items, function(i){
11968             
11969             var f = this.findField(i);
11970             
11971             if(!f){
11972                 return;
11973             }
11974             
11975             f.hide();
11976             
11977         }, this);
11978     },
11979     
11980     showFields : function(items)
11981     {
11982         Roo.each(items, function(i){
11983             
11984             var f = this.findField(i);
11985             
11986             if(!f){
11987                 return;
11988             }
11989             
11990             f.show();
11991             
11992         }, this);
11993     }
11994
11995 });
11996
11997 Roo.apply(Roo.bootstrap.form.Form, {
11998     
11999     popover : {
12000         
12001         padding : 5,
12002         
12003         isApplied : false,
12004         
12005         isMasked : false,
12006         
12007         form : false,
12008         
12009         target : false,
12010         
12011         toolTip : false,
12012         
12013         intervalID : false,
12014         
12015         maskEl : false,
12016         
12017         apply : function()
12018         {
12019             if(this.isApplied){
12020                 return;
12021             }
12022             
12023             this.maskEl = {
12024                 top : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-top-mask" }, true),
12025                 left : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-left-mask" }, true),
12026                 bottom : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-bottom-mask" }, true),
12027                 right : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-right-mask" }, true)
12028             };
12029             
12030             this.maskEl.top.enableDisplayMode("block");
12031             this.maskEl.left.enableDisplayMode("block");
12032             this.maskEl.bottom.enableDisplayMode("block");
12033             this.maskEl.right.enableDisplayMode("block");
12034             
12035             this.toolTip = new Roo.bootstrap.Tooltip({
12036                 cls : 'roo-form-error-popover',
12037                 alignment : {
12038                     'left' : ['r-l', [-2,0], 'right'],
12039                     'right' : ['l-r', [2,0], 'left'],
12040                     'bottom' : ['tl-bl', [0,2], 'top'],
12041                     'top' : [ 'bl-tl', [0,-2], 'bottom']
12042                 }
12043             });
12044             
12045             this.toolTip.render(Roo.get(document.body));
12046
12047             this.toolTip.el.enableDisplayMode("block");
12048             
12049             Roo.get(document.body).on('click', function(){
12050                 this.unmask();
12051             }, this);
12052             
12053             Roo.get(document.body).on('touchstart', function(){
12054                 this.unmask();
12055             }, this);
12056             
12057             this.isApplied = true
12058         },
12059         
12060         mask : function(form, target)
12061         {
12062             this.form = form;
12063             
12064             this.target = target;
12065             
12066             if(!this.form.errorMask || !target.el){
12067                 return;
12068             }
12069             
12070             var scrollable = this.target.el.findScrollableParent() || this.target.el.findParent('div.modal', 100, true) || Roo.get(document.body);
12071             
12072             Roo.log(scrollable);
12073             
12074             var ot = this.target.el.calcOffsetsTo(scrollable);
12075             
12076             var scrollTo = ot[1] - this.form.maskOffset;
12077             
12078             scrollTo = Math.min(scrollTo, scrollable.dom.scrollHeight);
12079             
12080             scrollable.scrollTo('top', scrollTo);
12081             
12082             var box = this.target.el.getBox();
12083             Roo.log(box);
12084             var zIndex = Roo.bootstrap.Modal.zIndex++;
12085
12086             
12087             this.maskEl.top.setStyle('position', 'absolute');
12088             this.maskEl.top.setStyle('z-index', zIndex);
12089             this.maskEl.top.setSize(Roo.lib.Dom.getDocumentWidth(), box.y - this.padding);
12090             this.maskEl.top.setLeft(0);
12091             this.maskEl.top.setTop(0);
12092             this.maskEl.top.show();
12093             
12094             this.maskEl.left.setStyle('position', 'absolute');
12095             this.maskEl.left.setStyle('z-index', zIndex);
12096             this.maskEl.left.setSize(box.x - this.padding, box.height + this.padding * 2);
12097             this.maskEl.left.setLeft(0);
12098             this.maskEl.left.setTop(box.y - this.padding);
12099             this.maskEl.left.show();
12100
12101             this.maskEl.bottom.setStyle('position', 'absolute');
12102             this.maskEl.bottom.setStyle('z-index', zIndex);
12103             this.maskEl.bottom.setSize(Roo.lib.Dom.getDocumentWidth(), Roo.lib.Dom.getDocumentHeight() - box.bottom - this.padding);
12104             this.maskEl.bottom.setLeft(0);
12105             this.maskEl.bottom.setTop(box.bottom + this.padding);
12106             this.maskEl.bottom.show();
12107
12108             this.maskEl.right.setStyle('position', 'absolute');
12109             this.maskEl.right.setStyle('z-index', zIndex);
12110             this.maskEl.right.setSize(Roo.lib.Dom.getDocumentWidth() - box.right - this.padding, box.height + this.padding * 2);
12111             this.maskEl.right.setLeft(box.right + this.padding);
12112             this.maskEl.right.setTop(box.y - this.padding);
12113             this.maskEl.right.show();
12114
12115             this.toolTip.bindEl = this.target.el;
12116
12117             this.toolTip.el.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
12118
12119             var tip = this.target.blankText;
12120
12121             if(this.target.getValue() !== '' ) {
12122                 
12123                 if (this.target.invalidText.length) {
12124                     tip = this.target.invalidText;
12125                 } else if (this.target.regexText.length){
12126                     tip = this.target.regexText;
12127                 }
12128             }
12129
12130             this.toolTip.show(tip);
12131
12132             this.intervalID = window.setInterval(function() {
12133                 Roo.bootstrap.form.Form.popover.unmask();
12134             }, 10000);
12135
12136             window.onwheel = function(){ return false;};
12137             
12138             (function(){ this.isMasked = true; }).defer(500, this);
12139             
12140         },
12141         
12142         unmask : function()
12143         {
12144             if(!this.isApplied || !this.isMasked || !this.form || !this.target || !this.form.errorMask){
12145                 return;
12146             }
12147             
12148             this.maskEl.top.setStyle('position', 'absolute');
12149             this.maskEl.top.setSize(0, 0).setXY([0, 0]);
12150             this.maskEl.top.hide();
12151
12152             this.maskEl.left.setStyle('position', 'absolute');
12153             this.maskEl.left.setSize(0, 0).setXY([0, 0]);
12154             this.maskEl.left.hide();
12155
12156             this.maskEl.bottom.setStyle('position', 'absolute');
12157             this.maskEl.bottom.setSize(0, 0).setXY([0, 0]);
12158             this.maskEl.bottom.hide();
12159
12160             this.maskEl.right.setStyle('position', 'absolute');
12161             this.maskEl.right.setSize(0, 0).setXY([0, 0]);
12162             this.maskEl.right.hide();
12163             
12164             this.toolTip.hide();
12165             
12166             this.toolTip.el.hide();
12167             
12168             window.onwheel = function(){ return true;};
12169             
12170             if(this.intervalID){
12171                 window.clearInterval(this.intervalID);
12172                 this.intervalID = false;
12173             }
12174             
12175             this.isMasked = false;
12176             
12177         }
12178         
12179     }
12180     
12181 });
12182
12183 /*
12184  * Based on:
12185  * Ext JS Library 1.1.1
12186  * Copyright(c) 2006-2007, Ext JS, LLC.
12187  *
12188  * Originally Released Under LGPL - original licence link has changed is not relivant.
12189  *
12190  * Fork - LGPL
12191  * <script type="text/javascript">
12192  */
12193 /**
12194  * @class Roo.form.VTypes
12195  * Overridable validation definitions. The validations provided are basic and intended to be easily customizable and extended.
12196  * @static
12197  */
12198 Roo.form.VTypes = function(){
12199     // closure these in so they are only created once.
12200     var alpha = /^[a-zA-Z_]+$/;
12201     var alphanum = /^[a-zA-Z0-9_]+$/;
12202     var email = /^([\w]+)(.[\w]+)*@([\w-]+\.){1,5}([A-Za-z]){2,24}$/;
12203     var url = /(((https?)|(ftp)):\/\/([\-\w]+\.)+\w{2,3}(\/[%\-\w]+(\.\w{2,})?)*(([\w\-\.\?\\\/+@&#;`~=%!]*)(\.\w{2,})?)*\/?)/i;
12204
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'){
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'){
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             this.markValid();
12959             return true;
12960         }
12961         
12962         this.markInvalid();
12963         return false;
12964     },
12965     
12966     
12967     /**
12968      * Validates a value according to the field's validation rules and marks the field as invalid
12969      * if the validation fails
12970      * @param {Mixed} value The value to validate
12971      * @return {Boolean} True if the value is valid, else false
12972      */
12973     validateValue : function(value)
12974     {
12975         if(this.getVisibilityEl().hasClass('hidden')){
12976             return true;
12977         }
12978         
12979         if(value.length < 1)  { // if it's blank
12980             if(this.allowBlank){
12981                 return true;
12982             }
12983             return false;
12984         }
12985         
12986         if(value.length < this.minLength){
12987             return false;
12988         }
12989         if(value.length > this.maxLength){
12990             return false;
12991         }
12992         if(this.vtype){
12993             var vt = Roo.form.VTypes;
12994             if(!vt[this.vtype](value, this)){
12995                 return false;
12996             }
12997         }
12998         if(typeof this.validator == "function"){
12999             var msg = this.validator(value);
13000             if (typeof(msg) == 'string') {
13001                 this.invalidText = msg;
13002             }
13003             if(msg !== true){
13004                 return false;
13005             }
13006         }
13007         
13008         if(this.regex && !this.regex.test(value)){
13009             return false;
13010         }
13011         
13012         return true;
13013     },
13014     
13015      // private
13016     fireKey : function(e){
13017         //Roo.log('field ' + e.getKey());
13018         if(e.isNavKeyPress()){
13019             this.fireEvent("specialkey", this, e);
13020         }
13021     },
13022     focus : function (selectText){
13023         if(this.rendered){
13024             this.inputEl().focus();
13025             if(selectText === true){
13026                 this.inputEl().dom.select();
13027             }
13028         }
13029         return this;
13030     } ,
13031     
13032     onFocus : function(){
13033         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
13034            // this.el.addClass(this.focusClass);
13035         }
13036         if(!this.hasFocus){
13037             this.hasFocus = true;
13038             this.startValue = this.getValue();
13039             this.fireEvent("focus", this);
13040         }
13041     },
13042     
13043     beforeBlur : Roo.emptyFn,
13044
13045     
13046     // private
13047     onBlur : function(){
13048         this.beforeBlur();
13049         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
13050             //this.el.removeClass(this.focusClass);
13051         }
13052         this.hasFocus = false;
13053         if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
13054             this.validate();
13055         }
13056         var v = this.getValue();
13057         if(String(v) !== String(this.startValue)){
13058             this.fireEvent('change', this, v, this.startValue);
13059         }
13060         this.fireEvent("blur", this);
13061     },
13062     
13063     onChange : function(e)
13064     {
13065         var v = this.getValue();
13066         if(String(v) !== String(this.startValue)){
13067             this.fireEvent('change', this, v, this.startValue);
13068         }
13069         
13070     },
13071     
13072     /**
13073      * Resets the current field value to the originally loaded value and clears any validation messages
13074      */
13075     reset : function(){
13076         this.setValue(this.originalValue);
13077         // this.validate();
13078         this.el.removeClass([this.invalidClass, this.validClass]);
13079         this.inputEl().removeClass(['is-valid', 'is-invalid']);
13080     },
13081      /**
13082      * Returns the name of the field
13083      * @return {Mixed} name The name field
13084      */
13085     getName: function(){
13086         return this.name;
13087     },
13088      /**
13089      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
13090      * @return {Mixed} value The field value
13091      */
13092     getValue : function(){
13093         var v = this.inputEl().getValue();
13094         return v;
13095     },
13096     /**
13097      * Returns the raw data value which may or may not be a valid, defined value.  To return a normalized value see {@link #getValue}.
13098      * @return {Mixed} value The field value
13099      */
13100     getRawValue : function(){
13101         var v = this.inputEl().getValue();
13102         
13103         return v;
13104     },
13105     
13106     /**
13107      * Sets the underlying DOM field's value directly, bypassing validation.  To set the value with validation see {@link #setValue}.
13108      * @param {Mixed} value The value to set
13109      */
13110     setRawValue : function(v){
13111         return this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
13112     },
13113     
13114     selectText : function(start, end){
13115         var v = this.getRawValue();
13116         if(v.length > 0){
13117             start = start === undefined ? 0 : start;
13118             end = end === undefined ? v.length : end;
13119             var d = this.inputEl().dom;
13120             if(d.setSelectionRange){
13121                 d.setSelectionRange(start, end);
13122             }else if(d.createTextRange){
13123                 var range = d.createTextRange();
13124                 range.moveStart("character", start);
13125                 range.moveEnd("character", v.length-end);
13126                 range.select();
13127             }
13128         }
13129     },
13130     
13131     /**
13132      * Sets a data value into the field and validates it.  To set the value directly without validation see {@link #setRawValue}.
13133      * @param {Mixed} value The value to set
13134      */
13135     setValue : function(v){
13136         this.value = v;
13137         if(this.rendered){
13138             this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
13139             this.validate();
13140         }
13141     },
13142     
13143     /*
13144     processValue : function(value){
13145         if(this.stripCharsRe){
13146             var newValue = value.replace(this.stripCharsRe, '');
13147             if(newValue !== value){
13148                 this.setRawValue(newValue);
13149                 return newValue;
13150             }
13151         }
13152         return value;
13153     },
13154   */
13155     preFocus : function(){
13156         
13157         if(this.selectOnFocus){
13158             this.inputEl().dom.select();
13159         }
13160     },
13161     filterKeys : function(e){
13162         var k = e.getKey();
13163         if(!Roo.isIE && (e.isNavKeyPress() || k == e.BACKSPACE || (k == e.DELETE && e.button == -1))){
13164             return;
13165         }
13166         var c = e.getCharCode(), cc = String.fromCharCode(c);
13167         if(Roo.isIE && (e.isSpecialKey() || !cc)){
13168             return;
13169         }
13170         if(!this.maskRe.test(cc)){
13171             e.stopEvent();
13172         }
13173     },
13174      /**
13175      * Clear any invalid styles/messages for this field
13176      */
13177     clearInvalid : function(){
13178         
13179         if(!this.el || this.preventMark){ // not rendered
13180             return;
13181         }
13182         
13183         
13184         this.el.removeClass([this.invalidClass, 'is-invalid']);
13185         
13186         if(this.hasFeedback && this.inputType != 'hidden'){
13187             
13188             var feedback = this.el.select('.form-control-feedback', true).first();
13189             
13190             if(feedback){
13191                 this.el.select('.form-control-feedback', true).first().removeClass(this.invalidFeedbackClass);
13192
13193                 feedback.update('');
13194             }
13195             
13196         }
13197         
13198         if(this.indicator){
13199             this.indicator.removeClass('visible');
13200             this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
13201         }
13202         
13203         this.fireEvent('valid', this);
13204     },
13205     
13206      /**
13207      * Mark this field as valid
13208      */
13209     markValid : function()
13210     {   
13211         if(!this.el  || this.preventMark){ // not rendered...
13212             return;
13213         }
13214         
13215         this.el.removeClass([this.invalidClass, this.validClass]);
13216         this.inputEl().removeClass(['is-valid', 'is-invalid']);
13217
13218         var feedback = this.el.select('.form-control-feedback', true).first();
13219             
13220         if(feedback){
13221             this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
13222             feedback.update('');
13223         }
13224         
13225         if(this.indicator){
13226             this.indicator.removeClass('visible');
13227             this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
13228         }
13229         
13230         if(this.disabled){
13231             return;
13232         }
13233         
13234            
13235         if(this.allowBlank && !this.getRawValue().length){
13236             return;
13237         }
13238         if (Roo.bootstrap.version == 3) {
13239             this.el.addClass(this.validClass);
13240         } else {
13241             this.inputEl().addClass('is-valid');
13242         }
13243
13244         if(this.hasFeedback && this.inputType != 'hidden'){
13245             
13246             var feedback = this.el.select('.form-control-feedback', true).first();
13247             
13248             if(feedback){
13249                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
13250                 this.el.select('.form-control-feedback', true).first().addClass([this.validFeedbackClass]);
13251             }
13252             
13253         }
13254         
13255         this.fireEvent('valid', this);
13256     },
13257     
13258      /**
13259      * Mark this field as invalid
13260      * @param {String} msg The validation message
13261      */
13262     markInvalid : function(msg)
13263     {
13264         if(!this.el  || this.preventMark){ // not rendered
13265             return;
13266         }
13267         
13268         this.el.removeClass([this.invalidClass, this.validClass]);
13269         this.inputEl().removeClass(['is-valid', 'is-invalid']);
13270         
13271         var feedback = this.el.select('.form-control-feedback', true).first();
13272             
13273         if(feedback){
13274             this.el.select('.form-control-feedback', true).first().removeClass(
13275                     [this.invalidFeedbackClass, this.validFeedbackClass]);
13276             feedback.update('');
13277         }
13278
13279         if(this.disabled){
13280             return;
13281         }
13282         
13283         if(this.allowBlank && !this.getRawValue().length){
13284             return;
13285         }
13286         
13287         if(this.indicator){
13288             this.indicator.removeClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
13289             this.indicator.addClass('visible');
13290         }
13291         if (Roo.bootstrap.version == 3) {
13292             this.el.addClass(this.invalidClass);
13293         } else {
13294             this.inputEl().addClass('is-invalid');
13295         }
13296         
13297         
13298         
13299         if(this.hasFeedback && this.inputType != 'hidden'){
13300             
13301             var feedback = this.el.select('.form-control-feedback', true).first();
13302             
13303             if(feedback){
13304                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
13305                 
13306                 this.el.select('.form-control-feedback', true).first().addClass([this.invalidFeedbackClass]);
13307
13308                 feedback.update(this.invalidText);
13309
13310                 if(!this.allowBlank && !this.getRawValue().length){
13311                     feedback.update(this.blankText);
13312                 }
13313                 
13314             }
13315             
13316         }
13317         
13318         this.fireEvent('invalid', this, msg);
13319     },
13320     // private
13321     SafariOnKeyDown : function(event)
13322     {
13323         // this is a workaround for a password hang bug on chrome/ webkit.
13324         if (this.inputEl().dom.type != 'password') {
13325             return;
13326         }
13327         
13328         var isSelectAll = false;
13329         
13330         if(this.inputEl().dom.selectionEnd > 0){
13331             isSelectAll = (this.inputEl().dom.selectionEnd - this.inputEl().dom.selectionStart - this.getValue().length == 0) ? true : false;
13332         }
13333         if(((event.getKey() == 8 || event.getKey() == 46) && this.getValue().length ==1)){ // backspace and delete key
13334             event.preventDefault();
13335             this.setValue('');
13336             return;
13337         }
13338         
13339         if(isSelectAll  && event.getCharCode() > 31 && !event.ctrlKey) { // not backspace and delete key (or ctrl-v)
13340             
13341             event.preventDefault();
13342             // this is very hacky as keydown always get's upper case.
13343             //
13344             var cc = String.fromCharCode(event.getCharCode());
13345             this.setValue( event.shiftKey ?  cc : cc.toLowerCase());
13346             
13347         }
13348     },
13349     adjustWidth : function(tag, w){
13350         tag = tag.toLowerCase();
13351         if(typeof w == 'number' && Roo.isStrict && !Roo.isSafari){
13352             if(Roo.isIE && (tag == 'input' || tag == 'textarea')){
13353                 if(tag == 'input'){
13354                     return w + 2;
13355                 }
13356                 if(tag == 'textarea'){
13357                     return w-2;
13358                 }
13359             }else if(Roo.isOpera){
13360                 if(tag == 'input'){
13361                     return w + 2;
13362                 }
13363                 if(tag == 'textarea'){
13364                     return w-2;
13365                 }
13366             }
13367         }
13368         return w;
13369     },
13370     
13371     setFieldLabel : function(v)
13372     {
13373         if(!this.rendered){
13374             return;
13375         }
13376         
13377         if(this.indicatorEl()){
13378             var ar = this.el.select('label > span',true);
13379             
13380             if (ar.elements.length) {
13381                 this.el.select('label > span',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
13382                 this.fieldLabel = v;
13383                 return;
13384             }
13385             
13386             var br = this.el.select('label',true);
13387             
13388             if(br.elements.length) {
13389                 this.el.select('label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
13390                 this.fieldLabel = v;
13391                 return;
13392             }
13393             
13394             Roo.log('Cannot Found any of label > span || label in input');
13395             return;
13396         }
13397         
13398         this.el.select('label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
13399         this.fieldLabel = v;
13400         
13401         
13402     }
13403 });
13404
13405  
13406 /*
13407  * - LGPL
13408  *
13409  * Input
13410  * 
13411  */
13412
13413 /**
13414  * @class Roo.bootstrap.form.TextArea
13415  * @extends Roo.bootstrap.form.Input
13416  * Bootstrap TextArea class
13417  * @cfg {Number} cols Specifies the visible width of a text area
13418  * @cfg {Number} rows Specifies the visible number of lines in a text area
13419  * @cfg {string} wrap (soft|hard)Specifies how the text in a text area is to be wrapped when submitted in a form
13420  * @cfg {string} resize (none|both|horizontal|vertical|inherit|initial)
13421  * @cfg {string} html text
13422  * 
13423  * @constructor
13424  * Create a new TextArea
13425  * @param {Object} config The config object
13426  */
13427
13428 Roo.bootstrap.form.TextArea = function(config){
13429     Roo.bootstrap.form.TextArea.superclass.constructor.call(this, config);
13430    
13431 };
13432
13433 Roo.extend(Roo.bootstrap.form.TextArea, Roo.bootstrap.form.Input,  {
13434      
13435     cols : false,
13436     rows : 5,
13437     readOnly : false,
13438     warp : 'soft',
13439     resize : false,
13440     value: false,
13441     html: false,
13442     
13443     getAutoCreate : function(){
13444         
13445         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
13446         
13447         var id = Roo.id();
13448         
13449         var cfg = {};
13450         
13451         if(this.inputType != 'hidden'){
13452             cfg.cls = 'form-group' //input-group
13453         }
13454         
13455         var input =  {
13456             tag: 'textarea',
13457             id : id,
13458             warp : this.warp,
13459             rows : this.rows,
13460             value : this.value || '',
13461             html: this.html || '',
13462             cls : 'form-control',
13463             placeholder : this.placeholder || '' 
13464             
13465         };
13466         
13467         if(this.maxLength && this.maxLength != Number.MAX_VALUE){
13468             input.maxLength = this.maxLength;
13469         }
13470         
13471         if(this.resize){
13472             input.style = (typeof(input.style) == 'undefined') ? 'resize:' + this.resize : input.style + 'resize:' + this.resize;
13473         }
13474         
13475         if(this.cols){
13476             input.cols = this.cols;
13477         }
13478         
13479         if (this.readOnly) {
13480             input.readonly = true;
13481         }
13482         
13483         if (this.name) {
13484             input.name = this.name;
13485         }
13486         
13487         if (this.size) {
13488             input.cls = (typeof(input.cls) == 'undefined') ? 'input-' + this.size : input.cls + ' input-' + this.size;
13489         }
13490         
13491         var settings=this;
13492         ['xs','sm','md','lg'].map(function(size){
13493             if (settings[size]) {
13494                 cfg.cls += ' col-' + size + '-' + settings[size];
13495             }
13496         });
13497         
13498         var inputblock = input;
13499         
13500         if(this.hasFeedback){
13501             
13502             var feedback = {
13503                 tag: 'span',
13504                 cls: 'glyphicon form-control-feedback'
13505             };
13506
13507             inputblock = {
13508                 cls : 'has-feedback',
13509                 cn :  [
13510                     input,
13511                     feedback
13512                 ] 
13513             };  
13514         }
13515         
13516         
13517         if (this.before || this.after) {
13518             
13519             inputblock = {
13520                 cls : 'input-group',
13521                 cn :  [] 
13522             };
13523             if (this.before) {
13524                 inputblock.cn.push({
13525                     tag :'span',
13526                     cls : 'input-group-addon',
13527                     html : this.before
13528                 });
13529             }
13530             
13531             inputblock.cn.push(input);
13532             
13533             if(this.hasFeedback){
13534                 inputblock.cls += ' has-feedback';
13535                 inputblock.cn.push(feedback);
13536             }
13537             
13538             if (this.after) {
13539                 inputblock.cn.push({
13540                     tag :'span',
13541                     cls : 'input-group-addon',
13542                     html : this.after
13543                 });
13544             }
13545             
13546         }
13547         
13548         
13549         cfg = this.getAutoCreateLabel( cfg, inputblock );
13550
13551          
13552         
13553         if (this.disabled) {
13554             input.disabled=true;
13555         }
13556         
13557         return cfg;
13558         
13559     },
13560     /**
13561      * return the real textarea element.
13562      */
13563     inputEl: function ()
13564     {
13565         return this.el.select('textarea.form-control',true).first();
13566     },
13567     
13568     /**
13569      * Clear any invalid styles/messages for this field
13570      */
13571     clearInvalid : function()
13572     {
13573         
13574         if(!this.el || this.preventMark){ // not rendered
13575             return;
13576         }
13577         
13578         var label = this.el.select('label', true).first();
13579         //var icon = this.el.select('i.fa-star', true).first();
13580         
13581         //if(label && icon){
13582         //    icon.remove();
13583         //}
13584         this.el.removeClass( this.validClass);
13585         this.inputEl().removeClass('is-invalid');
13586          
13587         if(this.hasFeedback && this.inputType != 'hidden'){
13588             
13589             var feedback = this.el.select('.form-control-feedback', true).first();
13590             
13591             if(feedback){
13592                 this.el.select('.form-control-feedback', true).first().removeClass(this.invalidFeedbackClass);
13593
13594                 feedback.update('');
13595             }
13596             
13597         }
13598         
13599         this.fireEvent('valid', this);
13600     },
13601     
13602      /**
13603      * Mark this field as valid
13604      */
13605     markValid : function()
13606     {
13607         if(!this.el  || this.preventMark){ // not rendered
13608             return;
13609         }
13610         
13611         this.el.removeClass([this.invalidClass, this.validClass]);
13612         this.inputEl().removeClass(['is-valid', 'is-invalid']);
13613         
13614         var feedback = this.el.select('.form-control-feedback', true).first();
13615             
13616         if(feedback){
13617             this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
13618             feedback.update('');
13619         }
13620
13621         if(this.disabled || this.allowBlank){
13622             return;
13623         }
13624         
13625         var label = this.el.select('label', true).first();
13626         var icon = this.el.select('i.fa-star', true).first();
13627         
13628         //if(label && icon){
13629         //    icon.remove();
13630         //}
13631         if (Roo.bootstrap.version == 3) {
13632             this.el.addClass(this.validClass);
13633         } else {
13634             this.inputEl().addClass('is-valid');
13635         }
13636         
13637         
13638         if(this.hasFeedback && this.inputType != 'hidden'){
13639             
13640             var feedback = this.el.select('.form-control-feedback', true).first();
13641             
13642             if(feedback){
13643                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
13644                 this.el.select('.form-control-feedback', true).first().addClass([this.validFeedbackClass]);
13645             }
13646             
13647         }
13648         
13649         this.fireEvent('valid', this);
13650     },
13651     
13652      /**
13653      * Mark this field as invalid
13654      * @param {String} msg The validation message
13655      */
13656     markInvalid : function(msg)
13657     {
13658         if(!this.el  || this.preventMark){ // not rendered
13659             return;
13660         }
13661         
13662         this.el.removeClass([this.invalidClass, this.validClass]);
13663         this.inputEl().removeClass(['is-valid', 'is-invalid']);
13664         
13665         var feedback = this.el.select('.form-control-feedback', true).first();
13666             
13667         if(feedback){
13668             this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
13669             feedback.update('');
13670         }
13671
13672         if(this.disabled){
13673             return;
13674         }
13675         
13676         var label = this.el.select('label', true).first();
13677         //var icon = this.el.select('i.fa-star', true).first();
13678         
13679         //if(!this.getValue().length && label && !icon){
13680           /*  this.el.createChild({
13681                 tag : 'i',
13682                 cls : 'text-danger fa fa-lg fa-star',
13683                 tooltip : 'This field is required',
13684                 style : 'margin-right:5px;'
13685             }, label, true);
13686             */
13687         //}
13688         
13689         if (Roo.bootstrap.version == 3) {
13690             this.el.addClass(this.invalidClass);
13691         } else {
13692             this.inputEl().addClass('is-invalid');
13693         }
13694         
13695         // fixme ... this may be depricated need to test..
13696         if(this.hasFeedback && this.inputType != 'hidden'){
13697             
13698             var feedback = this.el.select('.form-control-feedback', true).first();
13699             
13700             if(feedback){
13701                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
13702                 
13703                 this.el.select('.form-control-feedback', true).first().addClass([this.invalidFeedbackClass]);
13704
13705                 feedback.update(this.invalidText);
13706
13707                 if(!this.allowBlank && !this.getRawValue().length){
13708                     feedback.update(this.blankText);
13709                 }
13710                 
13711             }
13712             
13713         }
13714         
13715         this.fireEvent('invalid', this, msg);
13716     }
13717 });
13718
13719  
13720 /*
13721  * - LGPL
13722  *
13723  * trigger field - base class for combo..
13724  * 
13725  */
13726  
13727 /**
13728  * @class Roo.bootstrap.form.TriggerField
13729  * @extends Roo.bootstrap.form.Input
13730  * Provides a convenient wrapper for TextFields that adds a clickable trigger button (looks like a combobox by default).
13731  * The trigger has no default action, so you must assign a function to implement the trigger click handler by
13732  * overriding {@link #onTriggerClick}. You can create a TriggerField directly, as it renders exactly like a combobox
13733  * for which you can provide a custom implementation.  For example:
13734  * <pre><code>
13735 var trigger = new Roo.bootstrap.form.TriggerField();
13736 trigger.onTriggerClick = myTriggerFn;
13737 trigger.applyTo('my-field');
13738 </code></pre>
13739  *
13740  * However, in general you will most likely want to use TriggerField as the base class for a reusable component.
13741  * {@link Roo.bootstrap.form.DateField} and {@link Roo.bootstrap.form.ComboBox} are perfect examples of this.
13742  * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
13743  * class 'x-form-trigger' by default and triggerClass will be <b>appended</b> if specified.
13744  * @cfg {String} caret (search|calendar) BS3 only - carat fa name
13745
13746  * @constructor
13747  * Create a new TriggerField.
13748  * @param {Object} config Configuration options (valid {@Roo.bootstrap.form.Input} config options will also be applied
13749  * to the base TextField)
13750  */
13751 Roo.bootstrap.form.TriggerField = function(config){
13752     this.mimicing = false;
13753     Roo.bootstrap.form.TriggerField.superclass.constructor.call(this, config);
13754 };
13755
13756 Roo.extend(Roo.bootstrap.form.TriggerField, Roo.bootstrap.form.Input,  {
13757     /**
13758      * @cfg {String} triggerClass A CSS class to apply to the trigger
13759      */
13760      /**
13761      * @cfg {Boolean} hideTrigger True to hide the trigger element and display only the base text field (defaults to false)
13762      */
13763     hideTrigger:false,
13764
13765     /**
13766      * @cfg {Boolean} removable (true|false) special filter default false
13767      */
13768     removable : false,
13769     
13770     /** @cfg {Boolean} grow @hide */
13771     /** @cfg {Number} growMin @hide */
13772     /** @cfg {Number} growMax @hide */
13773
13774     /**
13775      * @hide 
13776      * @method
13777      */
13778     autoSize: Roo.emptyFn,
13779     // private
13780     monitorTab : true,
13781     // private
13782     deferHeight : true,
13783
13784     
13785     actionMode : 'wrap',
13786     
13787     caret : false,
13788     
13789     
13790     getAutoCreate : function(){
13791        
13792         var align = this.labelAlign || this.parentLabelAlign();
13793         
13794         var id = Roo.id();
13795         
13796         var cfg = {
13797             cls: 'form-group' //input-group
13798         };
13799         
13800         
13801         var input =  {
13802             tag: 'input',
13803             id : id,
13804             type : this.inputType,
13805             cls : 'form-control',
13806             autocomplete: 'new-password',
13807             placeholder : this.placeholder || '' 
13808             
13809         };
13810         if (this.name) {
13811             input.name = this.name;
13812         }
13813         if (this.size) {
13814             input.cls += ' input-' + this.size;
13815         }
13816         
13817         if (this.disabled) {
13818             input.disabled=true;
13819         }
13820         
13821         var inputblock = input;
13822         
13823         if(this.hasFeedback && !this.allowBlank){
13824             
13825             var feedback = {
13826                 tag: 'span',
13827                 cls: 'glyphicon form-control-feedback'
13828             };
13829             
13830             if(this.removable && !this.editable  ){
13831                 inputblock = {
13832                     cls : 'has-feedback',
13833                     cn :  [
13834                         inputblock,
13835                         {
13836                             tag: 'button',
13837                             html : 'x',
13838                             cls : 'roo-combo-removable-btn close'
13839                         },
13840                         feedback
13841                     ] 
13842                 };
13843             } else {
13844                 inputblock = {
13845                     cls : 'has-feedback',
13846                     cn :  [
13847                         inputblock,
13848                         feedback
13849                     ] 
13850                 };
13851             }
13852
13853         } else {
13854             if(this.removable && !this.editable ){
13855                 inputblock = {
13856                     cls : 'roo-removable',
13857                     cn :  [
13858                         inputblock,
13859                         {
13860                             tag: 'button',
13861                             html : 'x',
13862                             cls : 'roo-combo-removable-btn close'
13863                         }
13864                     ] 
13865                 };
13866             }
13867         }
13868         
13869         if (this.before || this.after) {
13870             
13871             inputblock = {
13872                 cls : 'input-group',
13873                 cn :  [] 
13874             };
13875             if (this.before) {
13876                 inputblock.cn.push({
13877                     tag :'span',
13878                     cls : 'input-group-addon input-group-prepend input-group-text',
13879                     html : this.before
13880                 });
13881             }
13882             
13883             inputblock.cn.push(input);
13884             
13885             if(this.hasFeedback && !this.allowBlank){
13886                 inputblock.cls += ' has-feedback';
13887                 inputblock.cn.push(feedback);
13888             }
13889             
13890             if (this.after) {
13891                 inputblock.cn.push({
13892                     tag :'span',
13893                     cls : 'input-group-addon input-group-append input-group-text',
13894                     html : this.after
13895                 });
13896             }
13897             
13898         };
13899         
13900       
13901         
13902         var ibwrap = inputblock;
13903         
13904         if(this.multiple){
13905             ibwrap = {
13906                 tag: 'ul',
13907                 cls: 'roo-select2-choices',
13908                 cn:[
13909                     {
13910                         tag: 'li',
13911                         cls: 'roo-select2-search-field',
13912                         cn: [
13913
13914                             inputblock
13915                         ]
13916                     }
13917                 ]
13918             };
13919                 
13920         }
13921         
13922         var combobox = {
13923             cls: 'roo-select2-container input-group',
13924             cn: [
13925                  {
13926                     tag: 'input',
13927                     type : 'hidden',
13928                     cls: 'form-hidden-field'
13929                 },
13930                 ibwrap
13931             ]
13932         };
13933         
13934         if(!this.multiple && this.showToggleBtn){
13935             
13936             var caret = {
13937                         tag: 'span',
13938                         cls: 'caret'
13939              };
13940             if (this.caret != false) {
13941                 caret = {
13942                      tag: 'i',
13943                      cls: 'fa fa-' + this.caret
13944                 };
13945                 
13946             }
13947             
13948             combobox.cn.push({
13949                 tag :'span',
13950                 cls : 'input-group-addon input-group-append input-group-text btn dropdown-toggle',
13951                 cn : [
13952                     Roo.bootstrap.version == 3 ? caret : '',
13953                     {
13954                         tag: 'span',
13955                         cls: 'combobox-clear',
13956                         cn  : [
13957                             {
13958                                 tag : 'i',
13959                                 cls: 'icon-remove'
13960                             }
13961                         ]
13962                     }
13963                 ]
13964
13965             })
13966         }
13967         
13968         if(this.multiple){
13969             combobox.cls += ' roo-select2-container-multi';
13970         }
13971          var indicator = {
13972             tag : 'i',
13973             cls : 'roo-required-indicator ' + (this.indicatorpos == 'right'  ? 'right' : 'left') +'-indicator text-danger fa fa-lg fa-star',
13974             tooltip : 'This field is required'
13975         };
13976       
13977         if (this.allowBlank) {
13978             indicator = {
13979                 tag : 'i',
13980                 style : 'display:none'
13981             };
13982         }
13983          
13984         
13985         
13986         if (align ==='left' && this.fieldLabel.length) {
13987             
13988             cfg.cls += ' roo-form-group-label-left'  + (Roo.bootstrap.version == 4 ? ' row' : '');
13989
13990             cfg.cn = [
13991                 indicator,
13992                 {
13993                     tag: 'label',
13994                     'for' :  id,
13995                     cls : 'control-label',
13996                     html : this.fieldLabel
13997
13998                 },
13999                 {
14000                     cls : "", 
14001                     cn: [
14002                         combobox
14003                     ]
14004                 }
14005
14006             ];
14007             
14008             var labelCfg = cfg.cn[1];
14009             var contentCfg = cfg.cn[2];
14010             
14011             if(this.indicatorpos == 'right'){
14012                 cfg.cn = [
14013                     {
14014                         tag: 'label',
14015                         'for' :  id,
14016                         cls : 'control-label',
14017                         cn : [
14018                             {
14019                                 tag : 'span',
14020                                 html : this.fieldLabel
14021                             },
14022                             indicator
14023                         ]
14024                     },
14025                     {
14026                         cls : "", 
14027                         cn: [
14028                             combobox
14029                         ]
14030                     }
14031
14032                 ];
14033                 
14034                 labelCfg = cfg.cn[0];
14035                 contentCfg = cfg.cn[1];
14036             }
14037             
14038             if(this.labelWidth > 12){
14039                 labelCfg.style = "width: " + this.labelWidth + 'px';
14040             }
14041             
14042             if(this.labelWidth < 13 && this.labelmd == 0){
14043                 this.labelmd = this.labelWidth;
14044             }
14045             
14046             if(this.labellg > 0){
14047                 labelCfg.cls += ' col-lg-' + this.labellg;
14048                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
14049             }
14050             
14051             if(this.labelmd > 0){
14052                 labelCfg.cls += ' col-md-' + this.labelmd;
14053                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
14054             }
14055             
14056             if(this.labelsm > 0){
14057                 labelCfg.cls += ' col-sm-' + this.labelsm;
14058                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
14059             }
14060             
14061             if(this.labelxs > 0){
14062                 labelCfg.cls += ' col-xs-' + this.labelxs;
14063                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
14064             }
14065             
14066         } else if ( this.fieldLabel.length) {
14067 //                Roo.log(" label");
14068             cfg.cn = [
14069                 indicator,
14070                {
14071                    tag: 'label',
14072                    //cls : 'input-group-addon',
14073                    html : this.fieldLabel
14074
14075                },
14076
14077                combobox
14078
14079             ];
14080             
14081             if(this.indicatorpos == 'right'){
14082                 
14083                 cfg.cn = [
14084                     {
14085                        tag: 'label',
14086                        cn : [
14087                            {
14088                                tag : 'span',
14089                                html : this.fieldLabel
14090                            },
14091                            indicator
14092                        ]
14093
14094                     },
14095                     combobox
14096
14097                 ];
14098
14099             }
14100
14101         } else {
14102             
14103 //                Roo.log(" no label && no align");
14104                 cfg = combobox
14105                      
14106                 
14107         }
14108         
14109         var settings=this;
14110         ['xs','sm','md','lg'].map(function(size){
14111             if (settings[size]) {
14112                 cfg.cls += ' col-' + size + '-' + settings[size];
14113             }
14114         });
14115         
14116         return cfg;
14117         
14118     },
14119     
14120     
14121     
14122     // private
14123     onResize : function(w, h){
14124 //        Roo.bootstrap.form.TriggerField.superclass.onResize.apply(this, arguments);
14125 //        if(typeof w == 'number'){
14126 //            var x = w - this.trigger.getWidth();
14127 //            this.inputEl().setWidth(this.adjustWidth('input', x));
14128 //            this.trigger.setStyle('left', x+'px');
14129 //        }
14130     },
14131
14132     // private
14133     adjustSize : Roo.BoxComponent.prototype.adjustSize,
14134
14135     // private
14136     getResizeEl : function(){
14137         return this.inputEl();
14138     },
14139
14140     // private
14141     getPositionEl : function(){
14142         return this.inputEl();
14143     },
14144
14145     // private
14146     alignErrorIcon : function(){
14147         this.errorIcon.alignTo(this.inputEl(), 'tl-tr', [2, 0]);
14148     },
14149
14150     // private
14151     initEvents : function(){
14152         
14153         this.createList();
14154         
14155         Roo.bootstrap.form.TriggerField.superclass.initEvents.call(this);
14156         //this.wrap = this.el.wrap({cls: "x-form-field-wrap"});
14157         if(!this.multiple && this.showToggleBtn){
14158             this.trigger = this.el.select('span.dropdown-toggle',true).first();
14159             if(this.hideTrigger){
14160                 this.trigger.setDisplayed(false);
14161             }
14162             this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
14163         }
14164         
14165         if(this.multiple){
14166             this.inputEl().on("click", this.onTriggerClick, this, {preventDefault:true});
14167         }
14168         
14169         if(this.removable && !this.editable && !this.tickable){
14170             var close = this.closeTriggerEl();
14171             
14172             if(close){
14173                 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
14174                 close.on('click', this.removeBtnClick, this, close);
14175             }
14176         }
14177         
14178         //this.trigger.addClassOnOver('x-form-trigger-over');
14179         //this.trigger.addClassOnClick('x-form-trigger-click');
14180         
14181         //if(!this.width){
14182         //    this.wrap.setWidth(this.el.getWidth()+this.trigger.getWidth());
14183         //}
14184     },
14185     
14186     closeTriggerEl : function()
14187     {
14188         var close = this.el.select('.roo-combo-removable-btn', true).first();
14189         return close ? close : false;
14190     },
14191     
14192     removeBtnClick : function(e, h, el)
14193     {
14194         e.preventDefault();
14195         
14196         if(this.fireEvent("remove", this) !== false){
14197             this.reset();
14198             this.fireEvent("afterremove", this)
14199         }
14200     },
14201     
14202     createList : function()
14203     {
14204         this.list = Roo.get(document.body).createChild({
14205             tag: Roo.bootstrap.version == 4 ? 'div' : 'ul',
14206             cls: 'typeahead typeahead-long dropdown-menu shadow',
14207             style: 'display:none'
14208         });
14209         
14210         this.list.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';;
14211         
14212     },
14213
14214     // private
14215     initTrigger : function(){
14216        
14217     },
14218
14219     // private
14220     onDestroy : function(){
14221         if(this.trigger){
14222             this.trigger.removeAllListeners();
14223           //  this.trigger.remove();
14224         }
14225         //if(this.wrap){
14226         //    this.wrap.remove();
14227         //}
14228         Roo.bootstrap.form.TriggerField.superclass.onDestroy.call(this);
14229     },
14230
14231     // private
14232     onFocus : function(){
14233         Roo.bootstrap.form.TriggerField.superclass.onFocus.call(this);
14234         /*
14235         if(!this.mimicing){
14236             this.wrap.addClass('x-trigger-wrap-focus');
14237             this.mimicing = true;
14238             Roo.get(Roo.isIE ? document.body : document).on("mousedown", this.mimicBlur, this);
14239             if(this.monitorTab){
14240                 this.el.on("keydown", this.checkTab, this);
14241             }
14242         }
14243         */
14244     },
14245
14246     // private
14247     checkTab : function(e){
14248         if(e.getKey() == e.TAB){
14249             this.triggerBlur();
14250         }
14251     },
14252
14253     // private
14254     onBlur : function(){
14255         // do nothing
14256     },
14257
14258     // private
14259     mimicBlur : function(e, t){
14260         /*
14261         if(!this.wrap.contains(t) && this.validateBlur()){
14262             this.triggerBlur();
14263         }
14264         */
14265     },
14266
14267     // private
14268     triggerBlur : function(){
14269         this.mimicing = false;
14270         Roo.get(Roo.isIE ? document.body : document).un("mousedown", this.mimicBlur);
14271         if(this.monitorTab){
14272             this.el.un("keydown", this.checkTab, this);
14273         }
14274         //this.wrap.removeClass('x-trigger-wrap-focus');
14275         Roo.bootstrap.form.TriggerField.superclass.onBlur.call(this);
14276     },
14277
14278     // private
14279     // This should be overriden by any subclass that needs to check whether or not the field can be blurred.
14280     validateBlur : function(e, t){
14281         return true;
14282     },
14283
14284     // private
14285     onDisable : function(){
14286         this.inputEl().dom.disabled = true;
14287         //Roo.bootstrap.form.TriggerField.superclass.onDisable.call(this);
14288         //if(this.wrap){
14289         //    this.wrap.addClass('x-item-disabled');
14290         //}
14291     },
14292
14293     // private
14294     onEnable : function(){
14295         this.inputEl().dom.disabled = false;
14296         //Roo.bootstrap.form.TriggerField.superclass.onEnable.call(this);
14297         //if(this.wrap){
14298         //    this.el.removeClass('x-item-disabled');
14299         //}
14300     },
14301
14302     // private
14303     onShow : function(){
14304         var ae = this.getActionEl();
14305         
14306         if(ae){
14307             ae.dom.style.display = '';
14308             ae.dom.style.visibility = 'visible';
14309         }
14310     },
14311
14312     // private
14313     
14314     onHide : function(){
14315         var ae = this.getActionEl();
14316         ae.dom.style.display = 'none';
14317     },
14318
14319     /**
14320      * The function that should handle the trigger's click event.  This method does nothing by default until overridden
14321      * by an implementing function.
14322      * @method
14323      * @param {EventObject} e
14324      */
14325     onTriggerClick : Roo.emptyFn
14326 });
14327  
14328 /*
14329 * Licence: LGPL
14330 */
14331
14332 /**
14333  * @class Roo.bootstrap.form.CardUploader
14334  * @extends Roo.bootstrap.Button
14335  * Bootstrap Card Uploader class - it's a button which when you add files to it, adds cards below with preview and the name...
14336  * @cfg {Number} errorTimeout default 3000
14337  * @cfg {Array}  images  an array of ?? Img objects ??? when loading existing files..
14338  * @cfg {Array}  html The button text.
14339
14340  *
14341  * @constructor
14342  * Create a new CardUploader
14343  * @param {Object} config The config object
14344  */
14345
14346 Roo.bootstrap.form.CardUploader = function(config){
14347     
14348  
14349     
14350     Roo.bootstrap.form.CardUploader.superclass.constructor.call(this, config);
14351     
14352     
14353     this.fileCollection   = new Roo.util.MixedCollection(false,function(r) {
14354         return r.data.id
14355      });
14356     
14357      this.addEvents({
14358          // raw events
14359         /**
14360          * @event preview
14361          * When a image is clicked on - and needs to display a slideshow or similar..
14362          * @param {Roo.bootstrap.Card} this
14363          * @param {Object} The image information data 
14364          *
14365          */
14366         'preview' : true,
14367          /**
14368          * @event download
14369          * When a the download link is clicked
14370          * @param {Roo.bootstrap.Card} this
14371          * @param {Object} The image information data  contains 
14372          */
14373         'download' : true
14374         
14375     });
14376 };
14377  
14378 Roo.extend(Roo.bootstrap.form.CardUploader, Roo.bootstrap.form.Input,  {
14379     
14380      
14381     errorTimeout : 3000,
14382      
14383     images : false,
14384    
14385     fileCollection : false,
14386     allowBlank : true,
14387     
14388     getAutoCreate : function()
14389     {
14390         
14391         var cfg =  {
14392             cls :'form-group' ,
14393             cn : [
14394                
14395                 {
14396                     tag: 'label',
14397                    //cls : 'input-group-addon',
14398                     html : this.fieldLabel
14399
14400                 },
14401
14402                 {
14403                     tag: 'input',
14404                     type : 'hidden',
14405                     name : this.name,
14406                     value : this.value,
14407                     cls : 'd-none  form-control'
14408                 },
14409                 
14410                 {
14411                     tag: 'input',
14412                     multiple : 'multiple',
14413                     type : 'file',
14414                     cls : 'd-none  roo-card-upload-selector'
14415                 },
14416                 
14417                 {
14418                     cls : 'roo-card-uploader-button-container w-100 mb-2'
14419                 },
14420                 {
14421                     cls : 'card-columns roo-card-uploader-container'
14422                 }
14423
14424             ]
14425         };
14426            
14427          
14428         return cfg;
14429     },
14430     
14431     getChildContainer : function() /// what children are added to.
14432     {
14433         return this.containerEl;
14434     },
14435    
14436     getButtonContainer : function() /// what children are added to.
14437     {
14438         return this.el.select(".roo-card-uploader-button-container").first();
14439     },
14440    
14441     initEvents : function()
14442     {
14443         
14444         Roo.bootstrap.form.Input.prototype.initEvents.call(this);
14445         
14446         var t = this;
14447         this.addxtype({
14448             xns: Roo.bootstrap,
14449
14450             xtype : 'Button',
14451             container_method : 'getButtonContainer' ,            
14452             html :  this.html, // fix changable?
14453             cls : 'w-100 ',
14454             listeners : {
14455                 'click' : function(btn, e) {
14456                     t.onClick(e);
14457                 }
14458             }
14459         });
14460         
14461         
14462         
14463         
14464         this.urlAPI = (window.createObjectURL && window) || 
14465                                 (window.URL && URL.revokeObjectURL && URL) || 
14466                                 (window.webkitURL && webkitURL);
14467                         
14468          
14469          
14470          
14471         this.selectorEl = this.el.select('.roo-card-upload-selector', true).first();
14472         
14473         this.selectorEl.on('change', this.onFileSelected, this);
14474         if (this.images) {
14475             var t = this;
14476             this.images.forEach(function(img) {
14477                 t.addCard(img)
14478             });
14479             this.images = false;
14480         }
14481         this.containerEl = this.el.select('.roo-card-uploader-container', true).first();
14482          
14483        
14484     },
14485     
14486    
14487     onClick : function(e)
14488     {
14489         e.preventDefault();
14490          
14491         this.selectorEl.dom.click();
14492          
14493     },
14494     
14495     onFileSelected : function(e)
14496     {
14497         e.preventDefault();
14498         
14499         if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
14500             return;
14501         }
14502         
14503         Roo.each(this.selectorEl.dom.files, function(file){    
14504             this.addFile(file);
14505         }, this);
14506          
14507     },
14508     
14509       
14510     
14511       
14512     
14513     addFile : function(file)
14514     {
14515            
14516         if(typeof(file) === 'string'){
14517             throw "Add file by name?"; // should not happen
14518             return;
14519         }
14520         
14521         if(!file || !this.urlAPI){
14522             return;
14523         }
14524         
14525         // file;
14526         // file.type;
14527         
14528         var _this = this;
14529         
14530         
14531         var url = _this.urlAPI.createObjectURL( file);
14532            
14533         this.addCard({
14534             id : Roo.bootstrap.form.CardUploader.ID--,
14535             is_uploaded : false,
14536             src : url,
14537             srcfile : file,
14538             title : file.name,
14539             mimetype : file.type,
14540             preview : false,
14541             is_deleted : 0
14542         });
14543         
14544     },
14545     
14546     /**
14547      * addCard - add an Attachment to the uploader
14548      * @param data - the data about the image to upload
14549      *
14550      * {
14551           id : 123
14552           title : "Title of file",
14553           is_uploaded : false,
14554           src : "http://.....",
14555           srcfile : { the File upload object },
14556           mimetype : file.type,
14557           preview : false,
14558           is_deleted : 0
14559           .. any other data...
14560         }
14561      *
14562      * 
14563     */
14564     
14565     addCard : function (data)
14566     {
14567         // hidden input element?
14568         // if the file is not an image...
14569         //then we need to use something other that and header_image
14570         var t = this;
14571         //   remove.....
14572         var footer = [
14573             {
14574                 xns : Roo.bootstrap,
14575                 xtype : 'CardFooter',
14576                  items: [
14577                     {
14578                         xns : Roo.bootstrap,
14579                         xtype : 'Element',
14580                         cls : 'd-flex',
14581                         items : [
14582                             
14583                             {
14584                                 xns : Roo.bootstrap,
14585                                 xtype : 'Button',
14586                                 html : String.format("<small>{0}</small>", data.title),
14587                                 cls : 'col-10 text-left',
14588                                 size: 'sm',
14589                                 weight: 'link',
14590                                 fa : 'download',
14591                                 listeners : {
14592                                     click : function() {
14593                                      
14594                                         t.fireEvent( "download", t, data );
14595                                     }
14596                                 }
14597                             },
14598                           
14599                             {
14600                                 xns : Roo.bootstrap,
14601                                 xtype : 'Button',
14602                                 style: 'max-height: 28px; ',
14603                                 size : 'sm',
14604                                 weight: 'danger',
14605                                 cls : 'col-2',
14606                                 fa : 'times',
14607                                 listeners : {
14608                                     click : function() {
14609                                         t.removeCard(data.id)
14610                                     }
14611                                 }
14612                             }
14613                         ]
14614                     }
14615                     
14616                 ] 
14617             }
14618             
14619         ];
14620         
14621         var cn = this.addxtype(
14622             {
14623                  
14624                 xns : Roo.bootstrap,
14625                 xtype : 'Card',
14626                 closeable : true,
14627                 header : !data.mimetype.match(/image/) && !data.preview ? "Document": false,
14628                 header_image : data.mimetype.match(/image/) ? data.src  : data.preview,
14629                 header_image_fit_square: true, // fixme  - we probably need to use the 'Img' element to do stuff like this.
14630                 data : data,
14631                 html : false,
14632                  
14633                 items : footer,
14634                 initEvents : function() {
14635                     Roo.bootstrap.Card.prototype.initEvents.call(this);
14636                     var card = this;
14637                     this.imgEl = this.el.select('.card-img-top').first();
14638                     if (this.imgEl) {
14639                         this.imgEl.on('click', function() { t.fireEvent( "preview", t, data ); }, this);
14640                         this.imgEl.set({ 'pointer' : 'cursor' });
14641                                   
14642                     }
14643                     this.getCardFooter().addClass('p-1');
14644                     
14645                   
14646                 }
14647                 
14648             }
14649         );
14650         // dont' really need ot update items.
14651         // this.items.push(cn);
14652         this.fileCollection.add(cn);
14653         
14654         if (!data.srcfile) {
14655             this.updateInput();
14656             return;
14657         }
14658             
14659         var _t = this;
14660         var reader = new FileReader();
14661         reader.addEventListener("load", function() {  
14662             data.srcdata =  reader.result;
14663             _t.updateInput();
14664         });
14665         reader.readAsDataURL(data.srcfile);
14666         
14667         
14668         
14669     },
14670     removeCard : function(id)
14671     {
14672         
14673         var card  = this.fileCollection.get(id);
14674         card.data.is_deleted = 1;
14675         card.data.src = ''; /// delete the source - so it reduces size of not uploaded images etc.
14676         //this.fileCollection.remove(card);
14677         //this.items = this.items.filter(function(e) { return e != card });
14678         // dont' really need ot update items.
14679         card.el.dom.parentNode.removeChild(card.el.dom);
14680         this.updateInput();
14681
14682         
14683     },
14684     reset: function()
14685     {
14686         this.fileCollection.each(function(card) {
14687             if (card.el.dom && card.el.dom.parentNode) {
14688                 card.el.dom.parentNode.removeChild(card.el.dom);
14689             }
14690         });
14691         this.fileCollection.clear();
14692         this.updateInput();
14693     },
14694     
14695     updateInput : function()
14696     {
14697          var data = [];
14698         this.fileCollection.each(function(e) {
14699             data.push(e.data);
14700             
14701         });
14702         this.inputEl().dom.value = JSON.stringify(data);
14703         
14704         
14705         
14706     }
14707     
14708     
14709 });
14710
14711
14712 Roo.bootstrap.form.CardUploader.ID = -1;/*
14713  * Based on:
14714  * Ext JS Library 1.1.1
14715  * Copyright(c) 2006-2007, Ext JS, LLC.
14716  *
14717  * Originally Released Under LGPL - original licence link has changed is not relivant.
14718  *
14719  * Fork - LGPL
14720  * <script type="text/javascript">
14721  */
14722
14723
14724 /**
14725  * @class Roo.data.SortTypes
14726  * @static
14727  * Defines the default sorting (casting?) comparison functions used when sorting data.
14728  */
14729 Roo.data.SortTypes = {
14730     /**
14731      * Default sort that does nothing
14732      * @param {Mixed} s The value being converted
14733      * @return {Mixed} The comparison value
14734      */
14735     none : function(s){
14736         return s;
14737     },
14738     
14739     /**
14740      * The regular expression used to strip tags
14741      * @type {RegExp}
14742      * @property
14743      */
14744     stripTagsRE : /<\/?[^>]+>/gi,
14745     
14746     /**
14747      * Strips all HTML tags to sort on text only
14748      * @param {Mixed} s The value being converted
14749      * @return {String} The comparison value
14750      */
14751     asText : function(s){
14752         return String(s).replace(this.stripTagsRE, "");
14753     },
14754     
14755     /**
14756      * Strips all HTML tags to sort on text only - Case insensitive
14757      * @param {Mixed} s The value being converted
14758      * @return {String} The comparison value
14759      */
14760     asUCText : function(s){
14761         return String(s).toUpperCase().replace(this.stripTagsRE, "");
14762     },
14763     
14764     /**
14765      * Case insensitive string
14766      * @param {Mixed} s The value being converted
14767      * @return {String} The comparison value
14768      */
14769     asUCString : function(s) {
14770         return String(s).toUpperCase();
14771     },
14772     
14773     /**
14774      * Date sorting
14775      * @param {Mixed} s The value being converted
14776      * @return {Number} The comparison value
14777      */
14778     asDate : function(s) {
14779         if(!s){
14780             return 0;
14781         }
14782         if(s instanceof Date){
14783             return s.getTime();
14784         }
14785         return Date.parse(String(s));
14786     },
14787     
14788     /**
14789      * Float sorting
14790      * @param {Mixed} s The value being converted
14791      * @return {Float} The comparison value
14792      */
14793     asFloat : function(s) {
14794         var val = parseFloat(String(s).replace(/,/g, ""));
14795         if(isNaN(val)) {
14796             val = 0;
14797         }
14798         return val;
14799     },
14800     
14801     /**
14802      * Integer sorting
14803      * @param {Mixed} s The value being converted
14804      * @return {Number} The comparison value
14805      */
14806     asInt : function(s) {
14807         var val = parseInt(String(s).replace(/,/g, ""));
14808         if(isNaN(val)) {
14809             val = 0;
14810         }
14811         return val;
14812     }
14813 };/*
14814  * Based on:
14815  * Ext JS Library 1.1.1
14816  * Copyright(c) 2006-2007, Ext JS, LLC.
14817  *
14818  * Originally Released Under LGPL - original licence link has changed is not relivant.
14819  *
14820  * Fork - LGPL
14821  * <script type="text/javascript">
14822  */
14823
14824 /**
14825 * @class Roo.data.Record
14826  * Instances of this class encapsulate both record <em>definition</em> information, and record
14827  * <em>value</em> information for use in {@link Roo.data.Store} objects, or any code which needs
14828  * to access Records cached in an {@link Roo.data.Store} object.<br>
14829  * <p>
14830  * Constructors for this class are generated by passing an Array of field definition objects to {@link #create}.
14831  * Instances are usually only created by {@link Roo.data.Reader} implementations when processing unformatted data
14832  * objects.<br>
14833  * <p>
14834  * Record objects generated by this constructor inherit all the methods of Roo.data.Record listed below.
14835  * @constructor
14836  * This constructor should not be used to create Record objects. Instead, use the constructor generated by
14837  * {@link #create}. The parameters are the same.
14838  * @param {Array} data An associative Array of data values keyed by the field name.
14839  * @param {Object} id (Optional) The id of the record. This id should be unique, and is used by the
14840  * {@link Roo.data.Store} object which owns the Record to index its collection of Records. If
14841  * not specified an integer id is generated.
14842  */
14843 Roo.data.Record = function(data, id){
14844     this.id = (id || id === 0) ? id : ++Roo.data.Record.AUTO_ID;
14845     this.data = data;
14846 };
14847
14848 /**
14849  * Generate a constructor for a specific record layout.
14850  * @param {Array} o An Array of field definition objects which specify field names, and optionally,
14851  * data types, and a mapping for an {@link Roo.data.Reader} to extract the field's value from a data object.
14852  * Each field definition object may contain the following properties: <ul>
14853  * <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,
14854  * for example the <em>dataIndex</em> property in column definition objects passed to {@link Roo.grid.ColumnModel}</p></li>
14855  * <li><b>mapping</b> : String<p style="margin-left:1em">(Optional) A path specification for use by the {@link Roo.data.Reader} implementation
14856  * that is creating the Record to access the data value from the data object. If an {@link Roo.data.JsonReader}
14857  * is being used, then this is a string containing the javascript expression to reference the data relative to 
14858  * the record item's root. If an {@link Roo.data.XmlReader} is being used, this is an {@link Roo.DomQuery} path
14859  * to the data item relative to the record element. If the mapping expression is the same as the field name,
14860  * this may be omitted.</p></li>
14861  * <li><b>type</b> : String<p style="margin-left:1em">(Optional) The data type for conversion to displayable value. Possible values are
14862  * <ul><li>auto (Default, implies no conversion)</li>
14863  * <li>string</li>
14864  * <li>int</li>
14865  * <li>float</li>
14866  * <li>boolean</li>
14867  * <li>date</li></ul></p></li>
14868  * <li><b>sortType</b> : Mixed<p style="margin-left:1em">(Optional) A member of {@link Roo.data.SortTypes}.</p></li>
14869  * <li><b>sortDir</b> : String<p style="margin-left:1em">(Optional) Initial direction to sort. "ASC" or "DESC"</p></li>
14870  * <li><b>convert</b> : Function<p style="margin-left:1em">(Optional) A function which converts the value provided
14871  * by the Reader into an object that will be stored in the Record. It is passed the
14872  * following parameters:<ul>
14873  * <li><b>v</b> : Mixed<p style="margin-left:1em">The data value as read by the Reader.</p></li>
14874  * </ul></p></li>
14875  * <li><b>dateFormat</b> : String<p style="margin-left:1em">(Optional) A format String for the Date.parseDate function.</p></li>
14876  * </ul>
14877  * <br>usage:<br><pre><code>
14878 var TopicRecord = Roo.data.Record.create(
14879     {name: 'title', mapping: 'topic_title'},
14880     {name: 'author', mapping: 'username'},
14881     {name: 'totalPosts', mapping: 'topic_replies', type: 'int'},
14882     {name: 'lastPost', mapping: 'post_time', type: 'date'},
14883     {name: 'lastPoster', mapping: 'user2'},
14884     {name: 'excerpt', mapping: 'post_text'}
14885 );
14886
14887 var myNewRecord = new TopicRecord({
14888     title: 'Do my job please',
14889     author: 'noobie',
14890     totalPosts: 1,
14891     lastPost: new Date(),
14892     lastPoster: 'Animal',
14893     excerpt: 'No way dude!'
14894 });
14895 myStore.add(myNewRecord);
14896 </code></pre>
14897  * @method create
14898  * @static
14899  */
14900 Roo.data.Record.create = function(o){
14901     var f = function(){
14902         f.superclass.constructor.apply(this, arguments);
14903     };
14904     Roo.extend(f, Roo.data.Record);
14905     var p = f.prototype;
14906     p.fields = new Roo.util.MixedCollection(false, function(field){
14907         return field.name;
14908     });
14909     for(var i = 0, len = o.length; i < len; i++){
14910         p.fields.add(new Roo.data.Field(o[i]));
14911     }
14912     f.getField = function(name){
14913         return p.fields.get(name);  
14914     };
14915     return f;
14916 };
14917
14918 Roo.data.Record.AUTO_ID = 1000;
14919 Roo.data.Record.EDIT = 'edit';
14920 Roo.data.Record.REJECT = 'reject';
14921 Roo.data.Record.COMMIT = 'commit';
14922
14923 Roo.data.Record.prototype = {
14924     /**
14925      * Readonly flag - true if this record has been modified.
14926      * @type Boolean
14927      */
14928     dirty : false,
14929     editing : false,
14930     error: null,
14931     modified: null,
14932
14933     // private
14934     join : function(store){
14935         this.store = store;
14936     },
14937
14938     /**
14939      * Set the named field to the specified value.
14940      * @param {String} name The name of the field to set.
14941      * @param {Object} value The value to set the field to.
14942      */
14943     set : function(name, value){
14944         if(this.data[name] == value){
14945             return;
14946         }
14947         this.dirty = true;
14948         if(!this.modified){
14949             this.modified = {};
14950         }
14951         if(typeof this.modified[name] == 'undefined'){
14952             this.modified[name] = this.data[name];
14953         }
14954         this.data[name] = value;
14955         if(!this.editing && this.store){
14956             this.store.afterEdit(this);
14957         }       
14958     },
14959
14960     /**
14961      * Get the value of the named field.
14962      * @param {String} name The name of the field to get the value of.
14963      * @return {Object} The value of the field.
14964      */
14965     get : function(name){
14966         return this.data[name]; 
14967     },
14968
14969     // private
14970     beginEdit : function(){
14971         this.editing = true;
14972         this.modified = {}; 
14973     },
14974
14975     // private
14976     cancelEdit : function(){
14977         this.editing = false;
14978         delete this.modified;
14979     },
14980
14981     // private
14982     endEdit : function(){
14983         this.editing = false;
14984         if(this.dirty && this.store){
14985             this.store.afterEdit(this);
14986         }
14987     },
14988
14989     /**
14990      * Usually called by the {@link Roo.data.Store} which owns the Record.
14991      * Rejects all changes made to the Record since either creation, or the last commit operation.
14992      * Modified fields are reverted to their original values.
14993      * <p>
14994      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
14995      * of reject operations.
14996      */
14997     reject : function(){
14998         var m = this.modified;
14999         for(var n in m){
15000             if(typeof m[n] != "function"){
15001                 this.data[n] = m[n];
15002             }
15003         }
15004         this.dirty = false;
15005         delete this.modified;
15006         this.editing = false;
15007         if(this.store){
15008             this.store.afterReject(this);
15009         }
15010     },
15011
15012     /**
15013      * Usually called by the {@link Roo.data.Store} which owns the Record.
15014      * Commits all changes made to the Record since either creation, or the last commit operation.
15015      * <p>
15016      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
15017      * of commit operations.
15018      */
15019     commit : function(){
15020         this.dirty = false;
15021         delete this.modified;
15022         this.editing = false;
15023         if(this.store){
15024             this.store.afterCommit(this);
15025         }
15026     },
15027
15028     // private
15029     hasError : function(){
15030         return this.error != null;
15031     },
15032
15033     // private
15034     clearError : function(){
15035         this.error = null;
15036     },
15037
15038     /**
15039      * Creates a copy of this record.
15040      * @param {String} id (optional) A new record id if you don't want to use this record's id
15041      * @return {Record}
15042      */
15043     copy : function(newId) {
15044         return new this.constructor(Roo.apply({}, this.data), newId || this.id);
15045     }
15046 };/*
15047  * Based on:
15048  * Ext JS Library 1.1.1
15049  * Copyright(c) 2006-2007, Ext JS, LLC.
15050  *
15051  * Originally Released Under LGPL - original licence link has changed is not relivant.
15052  *
15053  * Fork - LGPL
15054  * <script type="text/javascript">
15055  */
15056
15057
15058
15059 /**
15060  * @class Roo.data.Store
15061  * @extends Roo.util.Observable
15062  * The Store class encapsulates a client side cache of {@link Roo.data.Record} objects which provide input data
15063  * for widgets such as the Roo.grid.Grid, or the Roo.form.ComboBox.<br>
15064  * <p>
15065  * 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
15066  * has no knowledge of the format of the data returned by the Proxy.<br>
15067  * <p>
15068  * A Store object uses its configured implementation of {@link Roo.data.DataReader} to create {@link Roo.data.Record}
15069  * instances from the data object. These records are cached and made available through accessor functions.
15070  * @constructor
15071  * Creates a new Store.
15072  * @param {Object} config A config object containing the objects needed for the Store to access data,
15073  * and read the data into Records.
15074  */
15075 Roo.data.Store = function(config){
15076     this.data = new Roo.util.MixedCollection(false);
15077     this.data.getKey = function(o){
15078         return o.id;
15079     };
15080     this.baseParams = {};
15081     // private
15082     this.paramNames = {
15083         "start" : "start",
15084         "limit" : "limit",
15085         "sort" : "sort",
15086         "dir" : "dir",
15087         "multisort" : "_multisort"
15088     };
15089
15090     if(config && config.data){
15091         this.inlineData = config.data;
15092         delete config.data;
15093     }
15094
15095     Roo.apply(this, config);
15096     
15097     if(this.reader){ // reader passed
15098         this.reader = Roo.factory(this.reader, Roo.data);
15099         this.reader.xmodule = this.xmodule || false;
15100         if(!this.recordType){
15101             this.recordType = this.reader.recordType;
15102         }
15103         if(this.reader.onMetaChange){
15104             this.reader.onMetaChange = this.onMetaChange.createDelegate(this);
15105         }
15106     }
15107
15108     if(this.recordType){
15109         this.fields = this.recordType.prototype.fields;
15110     }
15111     this.modified = [];
15112
15113     this.addEvents({
15114         /**
15115          * @event datachanged
15116          * Fires when the data cache has changed, and a widget which is using this Store
15117          * as a Record cache should refresh its view.
15118          * @param {Store} this
15119          */
15120         datachanged : true,
15121         /**
15122          * @event metachange
15123          * Fires when this store's reader provides new metadata (fields). This is currently only support for JsonReaders.
15124          * @param {Store} this
15125          * @param {Object} meta The JSON metadata
15126          */
15127         metachange : true,
15128         /**
15129          * @event add
15130          * Fires when Records have been added to the Store
15131          * @param {Store} this
15132          * @param {Roo.data.Record[]} records The array of Records added
15133          * @param {Number} index The index at which the record(s) were added
15134          */
15135         add : true,
15136         /**
15137          * @event remove
15138          * Fires when a Record has been removed from the Store
15139          * @param {Store} this
15140          * @param {Roo.data.Record} record The Record that was removed
15141          * @param {Number} index The index at which the record was removed
15142          */
15143         remove : true,
15144         /**
15145          * @event update
15146          * Fires when a Record has been updated
15147          * @param {Store} this
15148          * @param {Roo.data.Record} record The Record that was updated
15149          * @param {String} operation The update operation being performed.  Value may be one of:
15150          * <pre><code>
15151  Roo.data.Record.EDIT
15152  Roo.data.Record.REJECT
15153  Roo.data.Record.COMMIT
15154          * </code></pre>
15155          */
15156         update : true,
15157         /**
15158          * @event clear
15159          * Fires when the data cache has been cleared.
15160          * @param {Store} this
15161          */
15162         clear : true,
15163         /**
15164          * @event beforeload
15165          * Fires before a request is made for a new data object.  If the beforeload handler returns false
15166          * the load action will be canceled.
15167          * @param {Store} this
15168          * @param {Object} options The loading options that were specified (see {@link #load} for details)
15169          */
15170         beforeload : true,
15171         /**
15172          * @event beforeloadadd
15173          * Fires after a new set of Records has been loaded.
15174          * @param {Store} this
15175          * @param {Roo.data.Record[]} records The Records that were loaded
15176          * @param {Object} options The loading options that were specified (see {@link #load} for details)
15177          */
15178         beforeloadadd : true,
15179         /**
15180          * @event load
15181          * Fires after a new set of Records has been loaded, before they are added to the store.
15182          * @param {Store} this
15183          * @param {Roo.data.Record[]} records The Records that were loaded
15184          * @param {Object} options The loading options that were specified (see {@link #load} for details)
15185          * @params {Object} return from reader
15186          */
15187         load : true,
15188         /**
15189          * @event loadexception
15190          * Fires if an exception occurs in the Proxy during loading.
15191          * Called with the signature of the Proxy's "loadexception" event.
15192          * If you return Json { data: [] , success: false, .... } then this will be thrown with the following args
15193          * 
15194          * @param {Proxy} 
15195          * @param {Object} ret return data from JsonData.reader() - success, totalRecords, records
15196          * @param {Object} opts - load Options
15197          * @param {Object} jsonData from your request (normally this contains the Exception)
15198          */
15199         loadexception : true
15200     });
15201     
15202     if(this.proxy){
15203         this.proxy = Roo.factory(this.proxy, Roo.data);
15204         this.proxy.xmodule = this.xmodule || false;
15205         this.relayEvents(this.proxy,  ["loadexception"]);
15206     }
15207     this.sortToggle = {};
15208     this.sortOrder = []; // array of order of sorting - updated by grid if multisort is enabled.
15209
15210     Roo.data.Store.superclass.constructor.call(this);
15211
15212     if(this.inlineData){
15213         this.loadData(this.inlineData);
15214         delete this.inlineData;
15215     }
15216 };
15217
15218 Roo.extend(Roo.data.Store, Roo.util.Observable, {
15219      /**
15220     * @cfg {boolean} isLocal   flag if data is locally available (and can be always looked up
15221     * without a remote query - used by combo/forms at present.
15222     */
15223     
15224     /**
15225     * @cfg {Roo.data.DataProxy} proxy [required] The Proxy object which provides access to a data object.
15226     */
15227     /**
15228     * @cfg {Array} data Inline data to be loaded when the store is initialized.
15229     */
15230     /**
15231     * @cfg {Roo.data.DataReader} reader [required]  The Reader object which processes the data object and returns
15232     * an Array of Roo.data.record objects which are cached keyed by their <em>id</em> property.
15233     */
15234     /**
15235     * @cfg {Object} baseParams An object containing properties which are to be sent as parameters
15236     * on any HTTP request
15237     */
15238     /**
15239     * @cfg {Object} sortInfo A config object in the format: {field: "fieldName", direction: "ASC|DESC"}
15240     */
15241     /**
15242     * @cfg {Boolean} multiSort enable multi column sorting (sort is based on the order of columns, remote only at present)
15243     */
15244     multiSort: false,
15245     /**
15246     * @cfg {boolean} remoteSort True if sorting is to be handled by requesting the Proxy to provide a refreshed
15247     * version of the data object in sorted order, as opposed to sorting the Record cache in place (defaults to false).
15248     */
15249     remoteSort : false,
15250
15251     /**
15252     * @cfg {boolean} pruneModifiedRecords True to clear all modified record information each time the store is
15253      * loaded or when a record is removed. (defaults to false).
15254     */
15255     pruneModifiedRecords : false,
15256
15257     // private
15258     lastOptions : null,
15259
15260     /**
15261      * Add Records to the Store and fires the add event.
15262      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
15263      */
15264     add : function(records){
15265         records = [].concat(records);
15266         for(var i = 0, len = records.length; i < len; i++){
15267             records[i].join(this);
15268         }
15269         var index = this.data.length;
15270         this.data.addAll(records);
15271         this.fireEvent("add", this, records, index);
15272     },
15273
15274     /**
15275      * Remove a Record from the Store and fires the remove event.
15276      * @param {Ext.data.Record} record The Roo.data.Record object to remove from the cache.
15277      */
15278     remove : function(record){
15279         var index = this.data.indexOf(record);
15280         this.data.removeAt(index);
15281  
15282         if(this.pruneModifiedRecords){
15283             this.modified.remove(record);
15284         }
15285         this.fireEvent("remove", this, record, index);
15286     },
15287
15288     /**
15289      * Remove all Records from the Store and fires the clear event.
15290      */
15291     removeAll : function(){
15292         this.data.clear();
15293         if(this.pruneModifiedRecords){
15294             this.modified = [];
15295         }
15296         this.fireEvent("clear", this);
15297     },
15298
15299     /**
15300      * Inserts Records to the Store at the given index and fires the add event.
15301      * @param {Number} index The start index at which to insert the passed Records.
15302      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
15303      */
15304     insert : function(index, records){
15305         records = [].concat(records);
15306         for(var i = 0, len = records.length; i < len; i++){
15307             this.data.insert(index, records[i]);
15308             records[i].join(this);
15309         }
15310         this.fireEvent("add", this, records, index);
15311     },
15312
15313     /**
15314      * Get the index within the cache of the passed Record.
15315      * @param {Roo.data.Record} record The Roo.data.Record object to to find.
15316      * @return {Number} The index of the passed Record. Returns -1 if not found.
15317      */
15318     indexOf : function(record){
15319         return this.data.indexOf(record);
15320     },
15321
15322     /**
15323      * Get the index within the cache of the Record with the passed id.
15324      * @param {String} id The id of the Record to find.
15325      * @return {Number} The index of the Record. Returns -1 if not found.
15326      */
15327     indexOfId : function(id){
15328         return this.data.indexOfKey(id);
15329     },
15330
15331     /**
15332      * Get the Record with the specified id.
15333      * @param {String} id The id of the Record to find.
15334      * @return {Roo.data.Record} The Record with the passed id. Returns undefined if not found.
15335      */
15336     getById : function(id){
15337         return this.data.key(id);
15338     },
15339
15340     /**
15341      * Get the Record at the specified index.
15342      * @param {Number} index The index of the Record to find.
15343      * @return {Roo.data.Record} The Record at the passed index. Returns undefined if not found.
15344      */
15345     getAt : function(index){
15346         return this.data.itemAt(index);
15347     },
15348
15349     /**
15350      * Returns a range of Records between specified indices.
15351      * @param {Number} startIndex (optional) The starting index (defaults to 0)
15352      * @param {Number} endIndex (optional) The ending index (defaults to the last Record in the Store)
15353      * @return {Roo.data.Record[]} An array of Records
15354      */
15355     getRange : function(start, end){
15356         return this.data.getRange(start, end);
15357     },
15358
15359     // private
15360     storeOptions : function(o){
15361         o = Roo.apply({}, o);
15362         delete o.callback;
15363         delete o.scope;
15364         this.lastOptions = o;
15365     },
15366
15367     /**
15368      * Loads the Record cache from the configured Proxy using the configured Reader.
15369      * <p>
15370      * If using remote paging, then the first load call must specify the <em>start</em>
15371      * and <em>limit</em> properties in the options.params property to establish the initial
15372      * position within the dataset, and the number of Records to cache on each read from the Proxy.
15373      * <p>
15374      * <strong>It is important to note that for remote data sources, loading is asynchronous,
15375      * and this call will return before the new data has been loaded. Perform any post-processing
15376      * in a callback function, or in a "load" event handler.</strong>
15377      * <p>
15378      * @param {Object} options An object containing properties which control loading options:<ul>
15379      * <li>params {Object} An object containing properties to pass as HTTP parameters to a remote data source.</li>
15380      * <li>params.data {Object} if you are using a MemoryProxy / JsonReader, use this as the data to load stuff..
15381      * <pre>
15382                 {
15383                     data : data,  // array of key=>value data like JsonReader
15384                     total : data.length,
15385                     success : true
15386                     
15387                 }
15388         </pre>
15389             }.</li>
15390      * <li>callback {Function} A function to be called after the Records have been loaded. The callback is
15391      * passed the following arguments:<ul>
15392      * <li>r : Roo.data.Record[]</li>
15393      * <li>options: Options object from the load call</li>
15394      * <li>success: Boolean success indicator</li></ul></li>
15395      * <li>scope {Object} Scope with which to call the callback (defaults to the Store object)</li>
15396      * <li>add {Boolean} indicator to append loaded records rather than replace the current cache.</li>
15397      * </ul>
15398      */
15399     load : function(options){
15400         options = options || {};
15401         if(this.fireEvent("beforeload", this, options) !== false){
15402             this.storeOptions(options);
15403             var p = Roo.apply(options.params || {}, this.baseParams);
15404             // if meta was not loaded from remote source.. try requesting it.
15405             if (!this.reader.metaFromRemote) {
15406                 p._requestMeta = 1;
15407             }
15408             if(this.sortInfo && this.remoteSort){
15409                 var pn = this.paramNames;
15410                 p[pn["sort"]] = this.sortInfo.field;
15411                 p[pn["dir"]] = this.sortInfo.direction;
15412             }
15413             if (this.multiSort) {
15414                 var pn = this.paramNames;
15415                 p[pn["multisort"]] = Roo.encode( { sort : this.sortToggle, order: this.sortOrder });
15416             }
15417             
15418             this.proxy.load(p, this.reader, this.loadRecords, this, options);
15419         }
15420     },
15421
15422     /**
15423      * Reloads the Record cache from the configured Proxy using the configured Reader and
15424      * the options from the last load operation performed.
15425      * @param {Object} options (optional) An object containing properties which may override the options
15426      * used in the last load operation. See {@link #load} for details (defaults to null, in which case
15427      * the most recently used options are reused).
15428      */
15429     reload : function(options){
15430         this.load(Roo.applyIf(options||{}, this.lastOptions));
15431     },
15432
15433     // private
15434     // Called as a callback by the Reader during a load operation.
15435     loadRecords : function(o, options, success){
15436          
15437         if(!o){
15438             if(success !== false){
15439                 this.fireEvent("load", this, [], options, o);
15440             }
15441             if(options.callback){
15442                 options.callback.call(options.scope || this, [], options, false);
15443             }
15444             return;
15445         }
15446         // if data returned failure - throw an exception.
15447         if (o.success === false) {
15448             // show a message if no listener is registered.
15449             if (!this.hasListener('loadexception') && typeof(o.raw.errorMsg) != 'undefined') {
15450                     Roo.MessageBox.alert("Error loading",o.raw.errorMsg);
15451             }
15452             // loadmask wil be hooked into this..
15453             this.fireEvent("loadexception", this, o, options, o.raw.errorMsg);
15454             return;
15455         }
15456         var r = o.records, t = o.totalRecords || r.length;
15457         
15458         this.fireEvent("beforeloadadd", this, r, options, o);
15459         
15460         if(!options || options.add !== true){
15461             if(this.pruneModifiedRecords){
15462                 this.modified = [];
15463             }
15464             for(var i = 0, len = r.length; i < len; i++){
15465                 r[i].join(this);
15466             }
15467             if(this.snapshot){
15468                 this.data = this.snapshot;
15469                 delete this.snapshot;
15470             }
15471             this.data.clear();
15472             this.data.addAll(r);
15473             this.totalLength = t;
15474             this.applySort();
15475             this.fireEvent("datachanged", this);
15476         }else{
15477             this.totalLength = Math.max(t, this.data.length+r.length);
15478             this.add(r);
15479         }
15480         
15481         if(this.parent && !Roo.isIOS && !this.useNativeIOS && this.parent.emptyTitle.length) {
15482                 
15483             var e = new Roo.data.Record({});
15484
15485             e.set(this.parent.displayField, this.parent.emptyTitle);
15486             e.set(this.parent.valueField, '');
15487
15488             this.insert(0, e);
15489         }
15490             
15491         this.fireEvent("load", this, r, options, o);
15492         if(options.callback){
15493             options.callback.call(options.scope || this, r, options, true);
15494         }
15495     },
15496
15497
15498     /**
15499      * Loads data from a passed data block. A Reader which understands the format of the data
15500      * must have been configured in the constructor.
15501      * @param {Object} data The data block from which to read the Records.  The format of the data expected
15502      * is dependent on the type of Reader that is configured and should correspond to that Reader's readRecords parameter.
15503      * @param {Boolean} append (Optional) True to append the new Records rather than replace the existing cache.
15504      */
15505     loadData : function(o, append){
15506         var r = this.reader.readRecords(o);
15507         this.loadRecords(r, {add: append}, true);
15508     },
15509     
15510      /**
15511      * using 'cn' the nested child reader read the child array into it's child stores.
15512      * @param {Object} rec The record with a 'children array
15513      */
15514     loadDataFromChildren : function(rec)
15515     {
15516         this.loadData(this.reader.toLoadData(rec));
15517     },
15518     
15519
15520     /**
15521      * Gets the number of cached records.
15522      * <p>
15523      * <em>If using paging, this may not be the total size of the dataset. If the data object
15524      * used by the Reader contains the dataset size, then the getTotalCount() function returns
15525      * the data set size</em>
15526      */
15527     getCount : function(){
15528         return this.data.length || 0;
15529     },
15530
15531     /**
15532      * Gets the total number of records in the dataset as returned by the server.
15533      * <p>
15534      * <em>If using paging, for this to be accurate, the data object used by the Reader must contain
15535      * the dataset size</em>
15536      */
15537     getTotalCount : function(){
15538         return this.totalLength || 0;
15539     },
15540
15541     /**
15542      * Returns the sort state of the Store as an object with two properties:
15543      * <pre><code>
15544  field {String} The name of the field by which the Records are sorted
15545  direction {String} The sort order, "ASC" or "DESC"
15546      * </code></pre>
15547      */
15548     getSortState : function(){
15549         return this.sortInfo;
15550     },
15551
15552     // private
15553     applySort : function(){
15554         if(this.sortInfo && !this.remoteSort){
15555             var s = this.sortInfo, f = s.field;
15556             var st = this.fields.get(f).sortType;
15557             var fn = function(r1, r2){
15558                 var v1 = st(r1.data[f]), v2 = st(r2.data[f]);
15559                 return v1 > v2 ? 1 : (v1 < v2 ? -1 : 0);
15560             };
15561             this.data.sort(s.direction, fn);
15562             if(this.snapshot && this.snapshot != this.data){
15563                 this.snapshot.sort(s.direction, fn);
15564             }
15565         }
15566     },
15567
15568     /**
15569      * Sets the default sort column and order to be used by the next load operation.
15570      * @param {String} fieldName The name of the field to sort by.
15571      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
15572      */
15573     setDefaultSort : function(field, dir){
15574         this.sortInfo = {field: field, direction: dir ? dir.toUpperCase() : "ASC"};
15575     },
15576
15577     /**
15578      * Sort the Records.
15579      * If remote sorting is used, the sort is performed on the server, and the cache is
15580      * reloaded. If local sorting is used, the cache is sorted internally.
15581      * @param {String} fieldName The name of the field to sort by.
15582      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
15583      */
15584     sort : function(fieldName, dir){
15585         var f = this.fields.get(fieldName);
15586         if(!dir){
15587             this.sortToggle[f.name] = this.sortToggle[f.name] || f.sortDir;
15588             
15589             if(this.multiSort || (this.sortInfo && this.sortInfo.field == f.name) ){ // toggle sort dir
15590                 dir = (this.sortToggle[f.name] || "ASC").toggle("ASC", "DESC");
15591             }else{
15592                 dir = f.sortDir;
15593             }
15594         }
15595         this.sortToggle[f.name] = dir;
15596         this.sortInfo = {field: f.name, direction: dir};
15597         if(!this.remoteSort){
15598             this.applySort();
15599             this.fireEvent("datachanged", this);
15600         }else{
15601             this.load(this.lastOptions);
15602         }
15603     },
15604
15605     /**
15606      * Calls the specified function for each of the Records in the cache.
15607      * @param {Function} fn The function to call. The Record is passed as the first parameter.
15608      * Returning <em>false</em> aborts and exits the iteration.
15609      * @param {Object} scope (optional) The scope in which to call the function (defaults to the Record).
15610      */
15611     each : function(fn, scope){
15612         this.data.each(fn, scope);
15613     },
15614
15615     /**
15616      * Gets all records modified since the last commit.  Modified records are persisted across load operations
15617      * (e.g., during paging).
15618      * @return {Roo.data.Record[]} An array of Records containing outstanding modifications.
15619      */
15620     getModifiedRecords : function(){
15621         return this.modified;
15622     },
15623
15624     // private
15625     createFilterFn : function(property, value, anyMatch){
15626         if(!value.exec){ // not a regex
15627             value = String(value);
15628             if(value.length == 0){
15629                 return false;
15630             }
15631             value = new RegExp((anyMatch === true ? '' : '^') + Roo.escapeRe(value), "i");
15632         }
15633         return function(r){
15634             return value.test(r.data[property]);
15635         };
15636     },
15637
15638     /**
15639      * Sums the value of <i>property</i> for each record between start and end and returns the result.
15640      * @param {String} property A field on your records
15641      * @param {Number} start The record index to start at (defaults to 0)
15642      * @param {Number} end The last record index to include (defaults to length - 1)
15643      * @return {Number} The sum
15644      */
15645     sum : function(property, start, end){
15646         var rs = this.data.items, v = 0;
15647         start = start || 0;
15648         end = (end || end === 0) ? end : rs.length-1;
15649
15650         for(var i = start; i <= end; i++){
15651             v += (rs[i].data[property] || 0);
15652         }
15653         return v;
15654     },
15655
15656     /**
15657      * Filter the records by a specified property.
15658      * @param {String} field A field on your records
15659      * @param {String/RegExp} value Either a string that the field
15660      * should start with or a RegExp to test against the field
15661      * @param {Boolean} anyMatch True to match any part not just the beginning
15662      */
15663     filter : function(property, value, anyMatch){
15664         var fn = this.createFilterFn(property, value, anyMatch);
15665         return fn ? this.filterBy(fn) : this.clearFilter();
15666     },
15667
15668     /**
15669      * Filter by a function. The specified function will be called with each
15670      * record in this data source. If the function returns true the record is included,
15671      * otherwise it is filtered.
15672      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
15673      * @param {Object} scope (optional) The scope of the function (defaults to this)
15674      */
15675     filterBy : function(fn, scope){
15676         this.snapshot = this.snapshot || this.data;
15677         this.data = this.queryBy(fn, scope||this);
15678         this.fireEvent("datachanged", this);
15679     },
15680
15681     /**
15682      * Query the records by a specified property.
15683      * @param {String} field A field on your records
15684      * @param {String/RegExp} value Either a string that the field
15685      * should start with or a RegExp to test against the field
15686      * @param {Boolean} anyMatch True to match any part not just the beginning
15687      * @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
15688      */
15689     query : function(property, value, anyMatch){
15690         var fn = this.createFilterFn(property, value, anyMatch);
15691         return fn ? this.queryBy(fn) : this.data.clone();
15692     },
15693
15694     /**
15695      * Query by a function. The specified function will be called with each
15696      * record in this data source. If the function returns true the record is included
15697      * in the results.
15698      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
15699      * @param {Object} scope (optional) The scope of the function (defaults to this)
15700       @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
15701      **/
15702     queryBy : function(fn, scope){
15703         var data = this.snapshot || this.data;
15704         return data.filterBy(fn, scope||this);
15705     },
15706
15707     /**
15708      * Collects unique values for a particular dataIndex from this store.
15709      * @param {String} dataIndex The property to collect
15710      * @param {Boolean} allowNull (optional) Pass true to allow null, undefined or empty string values
15711      * @param {Boolean} bypassFilter (optional) Pass true to collect from all records, even ones which are filtered
15712      * @return {Array} An array of the unique values
15713      **/
15714     collect : function(dataIndex, allowNull, bypassFilter){
15715         var d = (bypassFilter === true && this.snapshot) ?
15716                 this.snapshot.items : this.data.items;
15717         var v, sv, r = [], l = {};
15718         for(var i = 0, len = d.length; i < len; i++){
15719             v = d[i].data[dataIndex];
15720             sv = String(v);
15721             if((allowNull || !Roo.isEmpty(v)) && !l[sv]){
15722                 l[sv] = true;
15723                 r[r.length] = v;
15724             }
15725         }
15726         return r;
15727     },
15728
15729     /**
15730      * Revert to a view of the Record cache with no filtering applied.
15731      * @param {Boolean} suppressEvent If true the filter is cleared silently without notifying listeners
15732      */
15733     clearFilter : function(suppressEvent){
15734         if(this.snapshot && this.snapshot != this.data){
15735             this.data = this.snapshot;
15736             delete this.snapshot;
15737             if(suppressEvent !== true){
15738                 this.fireEvent("datachanged", this);
15739             }
15740         }
15741     },
15742
15743     // private
15744     afterEdit : function(record){
15745         if(this.modified.indexOf(record) == -1){
15746             this.modified.push(record);
15747         }
15748         this.fireEvent("update", this, record, Roo.data.Record.EDIT);
15749     },
15750     
15751     // private
15752     afterReject : function(record){
15753         this.modified.remove(record);
15754         this.fireEvent("update", this, record, Roo.data.Record.REJECT);
15755     },
15756
15757     // private
15758     afterCommit : function(record){
15759         this.modified.remove(record);
15760         this.fireEvent("update", this, record, Roo.data.Record.COMMIT);
15761     },
15762
15763     /**
15764      * Commit all Records with outstanding changes. To handle updates for changes, subscribe to the
15765      * Store's "update" event, and perform updating when the third parameter is Roo.data.Record.COMMIT.
15766      */
15767     commitChanges : function(){
15768         var m = this.modified.slice(0);
15769         this.modified = [];
15770         for(var i = 0, len = m.length; i < len; i++){
15771             m[i].commit();
15772         }
15773     },
15774
15775     /**
15776      * Cancel outstanding changes on all changed records.
15777      */
15778     rejectChanges : function(){
15779         var m = this.modified.slice(0);
15780         this.modified = [];
15781         for(var i = 0, len = m.length; i < len; i++){
15782             m[i].reject();
15783         }
15784     },
15785
15786     onMetaChange : function(meta, rtype, o){
15787         this.recordType = rtype;
15788         this.fields = rtype.prototype.fields;
15789         delete this.snapshot;
15790         this.sortInfo = meta.sortInfo || this.sortInfo;
15791         this.modified = [];
15792         this.fireEvent('metachange', this, this.reader.meta);
15793     },
15794     
15795     moveIndex : function(data, type)
15796     {
15797         var index = this.indexOf(data);
15798         
15799         var newIndex = index + type;
15800         
15801         this.remove(data);
15802         
15803         this.insert(newIndex, data);
15804         
15805     }
15806 });/*
15807  * Based on:
15808  * Ext JS Library 1.1.1
15809  * Copyright(c) 2006-2007, Ext JS, LLC.
15810  *
15811  * Originally Released Under LGPL - original licence link has changed is not relivant.
15812  *
15813  * Fork - LGPL
15814  * <script type="text/javascript">
15815  */
15816
15817 /**
15818  * @class Roo.data.SimpleStore
15819  * @extends Roo.data.Store
15820  * Small helper class to make creating Stores from Array data easier.
15821  * @cfg {Number} id The array index of the record id. Leave blank to auto generate ids.
15822  * @cfg {Array} fields An array of field definition objects, or field name strings.
15823  * @cfg {Object} an existing reader (eg. copied from another store)
15824  * @cfg {Array} data The multi-dimensional array of data
15825  * @cfg {Roo.data.DataProxy} proxy [not-required]  
15826  * @cfg {Roo.data.Reader} reader  [not-required] 
15827  * @constructor
15828  * @param {Object} config
15829  */
15830 Roo.data.SimpleStore = function(config)
15831 {
15832     Roo.data.SimpleStore.superclass.constructor.call(this, {
15833         isLocal : true,
15834         reader: typeof(config.reader) != 'undefined' ? config.reader : new Roo.data.ArrayReader({
15835                 id: config.id
15836             },
15837             Roo.data.Record.create(config.fields)
15838         ),
15839         proxy : new Roo.data.MemoryProxy(config.data)
15840     });
15841     this.load();
15842 };
15843 Roo.extend(Roo.data.SimpleStore, Roo.data.Store);/*
15844  * Based on:
15845  * Ext JS Library 1.1.1
15846  * Copyright(c) 2006-2007, Ext JS, LLC.
15847  *
15848  * Originally Released Under LGPL - original licence link has changed is not relivant.
15849  *
15850  * Fork - LGPL
15851  * <script type="text/javascript">
15852  */
15853
15854 /**
15855 /**
15856  * @extends Roo.data.Store
15857  * @class Roo.data.JsonStore
15858  * Small helper class to make creating Stores for JSON data easier. <br/>
15859 <pre><code>
15860 var store = new Roo.data.JsonStore({
15861     url: 'get-images.php',
15862     root: 'images',
15863     fields: ['name', 'url', {name:'size', type: 'float'}, {name:'lastmod', type:'date'}]
15864 });
15865 </code></pre>
15866  * <b>Note: Although they are not listed, this class inherits all of the config options of Store,
15867  * JsonReader and HttpProxy (unless inline data is provided).</b>
15868  * @cfg {Array} fields An array of field definition objects, or field name strings.
15869  * @constructor
15870  * @param {Object} config
15871  */
15872 Roo.data.JsonStore = function(c){
15873     Roo.data.JsonStore.superclass.constructor.call(this, Roo.apply(c, {
15874         proxy: !c.data ? new Roo.data.HttpProxy({url: c.url}) : undefined,
15875         reader: new Roo.data.JsonReader(c, c.fields)
15876     }));
15877 };
15878 Roo.extend(Roo.data.JsonStore, Roo.data.Store);/*
15879  * Based on:
15880  * Ext JS Library 1.1.1
15881  * Copyright(c) 2006-2007, Ext JS, LLC.
15882  *
15883  * Originally Released Under LGPL - original licence link has changed is not relivant.
15884  *
15885  * Fork - LGPL
15886  * <script type="text/javascript">
15887  */
15888
15889  
15890 Roo.data.Field = function(config){
15891     if(typeof config == "string"){
15892         config = {name: config};
15893     }
15894     Roo.apply(this, config);
15895     
15896     if(!this.type){
15897         this.type = "auto";
15898     }
15899     
15900     var st = Roo.data.SortTypes;
15901     // named sortTypes are supported, here we look them up
15902     if(typeof this.sortType == "string"){
15903         this.sortType = st[this.sortType];
15904     }
15905     
15906     // set default sortType for strings and dates
15907     if(!this.sortType){
15908         switch(this.type){
15909             case "string":
15910                 this.sortType = st.asUCString;
15911                 break;
15912             case "date":
15913                 this.sortType = st.asDate;
15914                 break;
15915             default:
15916                 this.sortType = st.none;
15917         }
15918     }
15919
15920     // define once
15921     var stripRe = /[\$,%]/g;
15922
15923     // prebuilt conversion function for this field, instead of
15924     // switching every time we're reading a value
15925     if(!this.convert){
15926         var cv, dateFormat = this.dateFormat;
15927         switch(this.type){
15928             case "":
15929             case "auto":
15930             case undefined:
15931                 cv = function(v){ return v; };
15932                 break;
15933             case "string":
15934                 cv = function(v){ return (v === undefined || v === null) ? '' : String(v); };
15935                 break;
15936             case "int":
15937                 cv = function(v){
15938                     return v !== undefined && v !== null && v !== '' ?
15939                            parseInt(String(v).replace(stripRe, ""), 10) : '';
15940                     };
15941                 break;
15942             case "float":
15943                 cv = function(v){
15944                     return v !== undefined && v !== null && v !== '' ?
15945                            parseFloat(String(v).replace(stripRe, ""), 10) : ''; 
15946                     };
15947                 break;
15948             case "bool":
15949             case "boolean":
15950                 cv = function(v){ return v === true || v === "true" || v == 1; };
15951                 break;
15952             case "date":
15953                 cv = function(v){
15954                     if(!v){
15955                         return '';
15956                     }
15957                     if(v instanceof Date){
15958                         return v;
15959                     }
15960                     if(dateFormat){
15961                         if(dateFormat == "timestamp"){
15962                             return new Date(v*1000);
15963                         }
15964                         return Date.parseDate(v, dateFormat);
15965                     }
15966                     var parsed = Date.parse(v);
15967                     return parsed ? new Date(parsed) : null;
15968                 };
15969              break;
15970             
15971         }
15972         this.convert = cv;
15973     }
15974 };
15975
15976 Roo.data.Field.prototype = {
15977     dateFormat: null,
15978     defaultValue: "",
15979     mapping: null,
15980     sortType : null,
15981     sortDir : "ASC"
15982 };/*
15983  * Based on:
15984  * Ext JS Library 1.1.1
15985  * Copyright(c) 2006-2007, Ext JS, LLC.
15986  *
15987  * Originally Released Under LGPL - original licence link has changed is not relivant.
15988  *
15989  * Fork - LGPL
15990  * <script type="text/javascript">
15991  */
15992  
15993 // Base class for reading structured data from a data source.  This class is intended to be
15994 // extended (see ArrayReader, JsonReader and XmlReader) and should not be created directly.
15995
15996 /**
15997  * @class Roo.data.DataReader
15998  * @abstract
15999  * Base class for reading structured data from a data source.  This class is intended to be
16000  * extended (see {Roo.data.ArrayReader}, {Roo.data.JsonReader} and {Roo.data.XmlReader}) and should not be created directly.
16001  */
16002
16003 Roo.data.DataReader = function(meta, recordType){
16004     
16005     this.meta = meta;
16006     
16007     this.recordType = recordType instanceof Array ? 
16008         Roo.data.Record.create(recordType) : recordType;
16009 };
16010
16011 Roo.data.DataReader.prototype = {
16012     
16013     
16014     readerType : 'Data',
16015      /**
16016      * Create an empty record
16017      * @param {Object} data (optional) - overlay some values
16018      * @return {Roo.data.Record} record created.
16019      */
16020     newRow :  function(d) {
16021         var da =  {};
16022         this.recordType.prototype.fields.each(function(c) {
16023             switch( c.type) {
16024                 case 'int' : da[c.name] = 0; break;
16025                 case 'date' : da[c.name] = new Date(); break;
16026                 case 'float' : da[c.name] = 0.0; break;
16027                 case 'boolean' : da[c.name] = false; break;
16028                 default : da[c.name] = ""; break;
16029             }
16030             
16031         });
16032         return new this.recordType(Roo.apply(da, d));
16033     }
16034     
16035     
16036 };/*
16037  * Based on:
16038  * Ext JS Library 1.1.1
16039  * Copyright(c) 2006-2007, Ext JS, LLC.
16040  *
16041  * Originally Released Under LGPL - original licence link has changed is not relivant.
16042  *
16043  * Fork - LGPL
16044  * <script type="text/javascript">
16045  */
16046
16047 /**
16048  * @class Roo.data.DataProxy
16049  * @extends Roo.util.Observable
16050  * @abstract
16051  * This class is an abstract base class for implementations which provide retrieval of
16052  * unformatted data objects.<br>
16053  * <p>
16054  * DataProxy implementations are usually used in conjunction with an implementation of Roo.data.DataReader
16055  * (of the appropriate type which knows how to parse the data object) to provide a block of
16056  * {@link Roo.data.Records} to an {@link Roo.data.Store}.<br>
16057  * <p>
16058  * Custom implementations must implement the load method as described in
16059  * {@link Roo.data.HttpProxy#load}.
16060  */
16061 Roo.data.DataProxy = function(){
16062     this.addEvents({
16063         /**
16064          * @event beforeload
16065          * Fires before a network request is made to retrieve a data object.
16066          * @param {Object} This DataProxy object.
16067          * @param {Object} params The params parameter to the load function.
16068          */
16069         beforeload : true,
16070         /**
16071          * @event load
16072          * Fires before the load method's callback is called.
16073          * @param {Object} This DataProxy object.
16074          * @param {Object} o The data object.
16075          * @param {Object} arg The callback argument object passed to the load function.
16076          */
16077         load : true,
16078         /**
16079          * @event loadexception
16080          * Fires if an Exception occurs during data retrieval.
16081          * @param {Object} This DataProxy object.
16082          * @param {Object} o The data object.
16083          * @param {Object} arg The callback argument object passed to the load function.
16084          * @param {Object} e The Exception.
16085          */
16086         loadexception : true
16087     });
16088     Roo.data.DataProxy.superclass.constructor.call(this);
16089 };
16090
16091 Roo.extend(Roo.data.DataProxy, Roo.util.Observable);
16092
16093     /**
16094      * @cfg {void} listeners (Not available) Constructor blocks listeners from being set
16095      */
16096 /*
16097  * Based on:
16098  * Ext JS Library 1.1.1
16099  * Copyright(c) 2006-2007, Ext JS, LLC.
16100  *
16101  * Originally Released Under LGPL - original licence link has changed is not relivant.
16102  *
16103  * Fork - LGPL
16104  * <script type="text/javascript">
16105  */
16106 /**
16107  * @class Roo.data.MemoryProxy
16108  * @extends Roo.data.DataProxy
16109  * An implementation of Roo.data.DataProxy that simply passes the data specified in its constructor
16110  * to the Reader when its load method is called.
16111  * @constructor
16112  * @param {Object} config  A config object containing the objects needed for the Store to access data,
16113  */
16114 Roo.data.MemoryProxy = function(config){
16115     var data = config;
16116     if (typeof(config) != 'undefined' && typeof(config.data) != 'undefined') {
16117         data = config.data;
16118     }
16119     Roo.data.MemoryProxy.superclass.constructor.call(this);
16120     this.data = data;
16121 };
16122
16123 Roo.extend(Roo.data.MemoryProxy, Roo.data.DataProxy, {
16124     
16125     /**
16126      *  @cfg {Object} data The data object which the Reader uses to construct a block of Roo.data.Records.
16127      */
16128     /**
16129      * Load data from the requested source (in this case an in-memory
16130      * data object passed to the constructor), read the data object into
16131      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
16132      * process that block using the passed callback.
16133      * @param {Object} params This parameter is not used by the MemoryProxy class.
16134      * @param {Roo.data.DataReader} reader The Reader object which converts the data
16135      * object into a block of Roo.data.Records.
16136      * @param {Function} callback The function into which to pass the block of Roo.data.records.
16137      * The function must be passed <ul>
16138      * <li>The Record block object</li>
16139      * <li>The "arg" argument from the load function</li>
16140      * <li>A boolean success indicator</li>
16141      * </ul>
16142      * @param {Object} scope The scope in which to call the callback
16143      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
16144      */
16145     load : function(params, reader, callback, scope, arg){
16146         params = params || {};
16147         var result;
16148         try {
16149             result = reader.readRecords(params.data ? params.data :this.data);
16150         }catch(e){
16151             this.fireEvent("loadexception", this, arg, null, e);
16152             callback.call(scope, null, arg, false);
16153             return;
16154         }
16155         callback.call(scope, result, arg, true);
16156     },
16157     
16158     // private
16159     update : function(params, records){
16160         
16161     }
16162 });/*
16163  * Based on:
16164  * Ext JS Library 1.1.1
16165  * Copyright(c) 2006-2007, Ext JS, LLC.
16166  *
16167  * Originally Released Under LGPL - original licence link has changed is not relivant.
16168  *
16169  * Fork - LGPL
16170  * <script type="text/javascript">
16171  */
16172 /**
16173  * @class Roo.data.HttpProxy
16174  * @extends Roo.data.DataProxy
16175  * An implementation of {@link Roo.data.DataProxy} that reads a data object from an {@link Roo.data.Connection} object
16176  * configured to reference a certain URL.<br><br>
16177  * <p>
16178  * <em>Note that this class cannot be used to retrieve data from a domain other than the domain
16179  * from which the running page was served.<br><br>
16180  * <p>
16181  * For cross-domain access to remote data, use an {@link Roo.data.ScriptTagProxy}.</em><br><br>
16182  * <p>
16183  * Be aware that to enable the browser to parse an XML document, the server must set
16184  * the Content-Type header in the HTTP response to "text/xml".
16185  * @constructor
16186  * @param {Object} conn Connection config options to add to each request (e.g. {url: 'foo.php'} or
16187  * an {@link Roo.data.Connection} object.  If a Connection config is passed, the singleton {@link Roo.Ajax} object
16188  * will be used to make the request.
16189  */
16190 Roo.data.HttpProxy = function(conn){
16191     Roo.data.HttpProxy.superclass.constructor.call(this);
16192     // is conn a conn config or a real conn?
16193     this.conn = conn;
16194     this.useAjax = !conn || !conn.events;
16195   
16196 };
16197
16198 Roo.extend(Roo.data.HttpProxy, Roo.data.DataProxy, {
16199     // thse are take from connection...
16200     
16201     /**
16202      * @cfg {String} url  The default URL to be used for requests to the server. (defaults to undefined)
16203      */
16204     /**
16205      * @cfg {Object} extraParams  An object containing properties which are used as
16206      * extra parameters to each request made by this object. (defaults to undefined)
16207      */
16208     /**
16209      * @cfg {Object} defaultHeaders   An object containing request headers which are added
16210      *  to each request made by this object. (defaults to undefined)
16211      */
16212     /**
16213      * @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)
16214      */
16215     /**
16216      * @cfg {Number} timeout The timeout in milliseconds to be used for requests. (defaults to 30000)
16217      */
16218      /**
16219      * @cfg {Boolean} autoAbort Whether this request should abort any pending requests. (defaults to false)
16220      * @type Boolean
16221      */
16222   
16223
16224     /**
16225      * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
16226      * @type Boolean
16227      */
16228     /**
16229      * Return the {@link Roo.data.Connection} object being used by this Proxy.
16230      * @return {Connection} The Connection object. This object may be used to subscribe to events on
16231      * a finer-grained basis than the DataProxy events.
16232      */
16233     getConnection : function(){
16234         return this.useAjax ? Roo.Ajax : this.conn;
16235     },
16236
16237     /**
16238      * Load data from the configured {@link Roo.data.Connection}, read the data object into
16239      * a block of Roo.data.Records using the passed {@link Roo.data.DataReader} implementation, and
16240      * process that block using the passed callback.
16241      * @param {Object} params An object containing properties which are to be used as HTTP parameters
16242      * for the request to the remote server.
16243      * @param {Roo.data.DataReader} reader The Reader object which converts the data
16244      * object into a block of Roo.data.Records.
16245      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
16246      * The function must be passed <ul>
16247      * <li>The Record block object</li>
16248      * <li>The "arg" argument from the load function</li>
16249      * <li>A boolean success indicator</li>
16250      * </ul>
16251      * @param {Object} scope The scope in which to call the callback
16252      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
16253      */
16254     load : function(params, reader, callback, scope, arg){
16255         if(this.fireEvent("beforeload", this, params) !== false){
16256             var  o = {
16257                 params : params || {},
16258                 request: {
16259                     callback : callback,
16260                     scope : scope,
16261                     arg : arg
16262                 },
16263                 reader: reader,
16264                 callback : this.loadResponse,
16265                 scope: this
16266             };
16267             if(this.useAjax){
16268                 Roo.applyIf(o, this.conn);
16269                 if(this.activeRequest){
16270                     Roo.Ajax.abort(this.activeRequest);
16271                 }
16272                 this.activeRequest = Roo.Ajax.request(o);
16273             }else{
16274                 this.conn.request(o);
16275             }
16276         }else{
16277             callback.call(scope||this, null, arg, false);
16278         }
16279     },
16280
16281     // private
16282     loadResponse : function(o, success, response){
16283         delete this.activeRequest;
16284         if(!success){
16285             this.fireEvent("loadexception", this, o, response);
16286             o.request.callback.call(o.request.scope, null, o.request.arg, false);
16287             return;
16288         }
16289         var result;
16290         try {
16291             result = o.reader.read(response);
16292         }catch(e){
16293             o.success = false;
16294             o.raw = { errorMsg : response.responseText };
16295             this.fireEvent("loadexception", this, o, response, e);
16296             o.request.callback.call(o.request.scope, o, o.request.arg, false);
16297             return;
16298         }
16299         
16300         this.fireEvent("load", this, o, o.request.arg);
16301         o.request.callback.call(o.request.scope, result, o.request.arg, true);
16302     },
16303
16304     // private
16305     update : function(dataSet){
16306
16307     },
16308
16309     // private
16310     updateResponse : function(dataSet){
16311
16312     }
16313 });/*
16314  * Based on:
16315  * Ext JS Library 1.1.1
16316  * Copyright(c) 2006-2007, Ext JS, LLC.
16317  *
16318  * Originally Released Under LGPL - original licence link has changed is not relivant.
16319  *
16320  * Fork - LGPL
16321  * <script type="text/javascript">
16322  */
16323
16324 /**
16325  * @class Roo.data.ScriptTagProxy
16326  * An implementation of Roo.data.DataProxy that reads a data object from a URL which may be in a domain
16327  * other than the originating domain of the running page.<br><br>
16328  * <p>
16329  * <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
16330  * of the running page, you must use this class, rather than DataProxy.</em><br><br>
16331  * <p>
16332  * The content passed back from a server resource requested by a ScriptTagProxy is executable JavaScript
16333  * source code that is used as the source inside a &lt;script> tag.<br><br>
16334  * <p>
16335  * In order for the browser to process the returned data, the server must wrap the data object
16336  * with a call to a callback function, the name of which is passed as a parameter by the ScriptTagProxy.
16337  * Below is a Java example for a servlet which returns data for either a ScriptTagProxy, or an HttpProxy
16338  * depending on whether the callback name was passed:
16339  * <p>
16340  * <pre><code>
16341 boolean scriptTag = false;
16342 String cb = request.getParameter("callback");
16343 if (cb != null) {
16344     scriptTag = true;
16345     response.setContentType("text/javascript");
16346 } else {
16347     response.setContentType("application/x-json");
16348 }
16349 Writer out = response.getWriter();
16350 if (scriptTag) {
16351     out.write(cb + "(");
16352 }
16353 out.print(dataBlock.toJsonString());
16354 if (scriptTag) {
16355     out.write(");");
16356 }
16357 </pre></code>
16358  *
16359  * @constructor
16360  * @param {Object} config A configuration object.
16361  */
16362 Roo.data.ScriptTagProxy = function(config){
16363     Roo.data.ScriptTagProxy.superclass.constructor.call(this);
16364     Roo.apply(this, config);
16365     this.head = document.getElementsByTagName("head")[0];
16366 };
16367
16368 Roo.data.ScriptTagProxy.TRANS_ID = 1000;
16369
16370 Roo.extend(Roo.data.ScriptTagProxy, Roo.data.DataProxy, {
16371     /**
16372      * @cfg {String} url The URL from which to request the data object.
16373      */
16374     /**
16375      * @cfg {Number} timeout (Optional) The number of milliseconds to wait for a response. Defaults to 30 seconds.
16376      */
16377     timeout : 30000,
16378     /**
16379      * @cfg {String} callbackParam (Optional) The name of the parameter to pass to the server which tells
16380      * the server the name of the callback function set up by the load call to process the returned data object.
16381      * Defaults to "callback".<p>The server-side processing must read this parameter value, and generate
16382      * javascript output which calls this named function passing the data object as its only parameter.
16383      */
16384     callbackParam : "callback",
16385     /**
16386      *  @cfg {Boolean} nocache (Optional) Defaults to true. Disable cacheing by adding a unique parameter
16387      * name to the request.
16388      */
16389     nocache : true,
16390
16391     /**
16392      * Load data from the configured URL, read the data object into
16393      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
16394      * process that block using the passed callback.
16395      * @param {Object} params An object containing properties which are to be used as HTTP parameters
16396      * for the request to the remote server.
16397      * @param {Roo.data.DataReader} reader The Reader object which converts the data
16398      * object into a block of Roo.data.Records.
16399      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
16400      * The function must be passed <ul>
16401      * <li>The Record block object</li>
16402      * <li>The "arg" argument from the load function</li>
16403      * <li>A boolean success indicator</li>
16404      * </ul>
16405      * @param {Object} scope The scope in which to call the callback
16406      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
16407      */
16408     load : function(params, reader, callback, scope, arg){
16409         if(this.fireEvent("beforeload", this, params) !== false){
16410
16411             var p = Roo.urlEncode(Roo.apply(params, this.extraParams));
16412
16413             var url = this.url;
16414             url += (url.indexOf("?") != -1 ? "&" : "?") + p;
16415             if(this.nocache){
16416                 url += "&_dc=" + (new Date().getTime());
16417             }
16418             var transId = ++Roo.data.ScriptTagProxy.TRANS_ID;
16419             var trans = {
16420                 id : transId,
16421                 cb : "stcCallback"+transId,
16422                 scriptId : "stcScript"+transId,
16423                 params : params,
16424                 arg : arg,
16425                 url : url,
16426                 callback : callback,
16427                 scope : scope,
16428                 reader : reader
16429             };
16430             var conn = this;
16431
16432             window[trans.cb] = function(o){
16433                 conn.handleResponse(o, trans);
16434             };
16435
16436             url += String.format("&{0}={1}", this.callbackParam, trans.cb);
16437
16438             if(this.autoAbort !== false){
16439                 this.abort();
16440             }
16441
16442             trans.timeoutId = this.handleFailure.defer(this.timeout, this, [trans]);
16443
16444             var script = document.createElement("script");
16445             script.setAttribute("src", url);
16446             script.setAttribute("type", "text/javascript");
16447             script.setAttribute("id", trans.scriptId);
16448             this.head.appendChild(script);
16449
16450             this.trans = trans;
16451         }else{
16452             callback.call(scope||this, null, arg, false);
16453         }
16454     },
16455
16456     // private
16457     isLoading : function(){
16458         return this.trans ? true : false;
16459     },
16460
16461     /**
16462      * Abort the current server request.
16463      */
16464     abort : function(){
16465         if(this.isLoading()){
16466             this.destroyTrans(this.trans);
16467         }
16468     },
16469
16470     // private
16471     destroyTrans : function(trans, isLoaded){
16472         this.head.removeChild(document.getElementById(trans.scriptId));
16473         clearTimeout(trans.timeoutId);
16474         if(isLoaded){
16475             window[trans.cb] = undefined;
16476             try{
16477                 delete window[trans.cb];
16478             }catch(e){}
16479         }else{
16480             // if hasn't been loaded, wait for load to remove it to prevent script error
16481             window[trans.cb] = function(){
16482                 window[trans.cb] = undefined;
16483                 try{
16484                     delete window[trans.cb];
16485                 }catch(e){}
16486             };
16487         }
16488     },
16489
16490     // private
16491     handleResponse : function(o, trans){
16492         this.trans = false;
16493         this.destroyTrans(trans, true);
16494         var result;
16495         try {
16496             result = trans.reader.readRecords(o);
16497         }catch(e){
16498             this.fireEvent("loadexception", this, o, trans.arg, e);
16499             trans.callback.call(trans.scope||window, null, trans.arg, false);
16500             return;
16501         }
16502         this.fireEvent("load", this, o, trans.arg);
16503         trans.callback.call(trans.scope||window, result, trans.arg, true);
16504     },
16505
16506     // private
16507     handleFailure : function(trans){
16508         this.trans = false;
16509         this.destroyTrans(trans, false);
16510         this.fireEvent("loadexception", this, null, trans.arg);
16511         trans.callback.call(trans.scope||window, null, trans.arg, false);
16512     }
16513 });/*
16514  * Based on:
16515  * Ext JS Library 1.1.1
16516  * Copyright(c) 2006-2007, Ext JS, LLC.
16517  *
16518  * Originally Released Under LGPL - original licence link has changed is not relivant.
16519  *
16520  * Fork - LGPL
16521  * <script type="text/javascript">
16522  */
16523
16524 /**
16525  * @class Roo.data.JsonReader
16526  * @extends Roo.data.DataReader
16527  * Data reader class to create an Array of Roo.data.Record objects from a JSON response
16528  * based on mappings in a provided Roo.data.Record constructor.
16529  * 
16530  * The default behaviour of a store is to send ?_requestMeta=1, unless the class has recieved 'metaData' property
16531  * in the reply previously. 
16532  * 
16533  * <p>
16534  * Example code:
16535  * <pre><code>
16536 var RecordDef = Roo.data.Record.create([
16537     {name: 'name', mapping: 'name'},     // "mapping" property not needed if it's the same as "name"
16538     {name: 'occupation'}                 // This field will use "occupation" as the mapping.
16539 ]);
16540 var myReader = new Roo.data.JsonReader({
16541     totalProperty: "results",    // The property which contains the total dataset size (optional)
16542     root: "rows",                // The property which contains an Array of row objects
16543     id: "id"                     // The property within each row object that provides an ID for the record (optional)
16544 }, RecordDef);
16545 </code></pre>
16546  * <p>
16547  * This would consume a JSON file like this:
16548  * <pre><code>
16549 { 'results': 2, 'rows': [
16550     { 'id': 1, 'name': 'Bill', occupation: 'Gardener' },
16551     { 'id': 2, 'name': 'Ben', occupation: 'Horticulturalist' } ]
16552 }
16553 </code></pre>
16554  * @cfg {String} totalProperty Name of the property from which to retrieve the total number of records
16555  * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
16556  * paged from the remote server.
16557  * @cfg {String} successProperty Name of the property from which to retrieve the success attribute used by forms.
16558  * @cfg {String} root name of the property which contains the Array of row objects.
16559  * @cfg {String} id Name of the property within a row object that contains a record identifier value.
16560  * @cfg {Array} fields Array of field definition objects
16561  * @constructor
16562  * Create a new JsonReader
16563  * @param {Object} meta Metadata configuration options
16564  * @param {Object} recordType Either an Array of field definition objects,
16565  * or an {@link Roo.data.Record} object created using {@link Roo.data.Record#create}.
16566  */
16567 Roo.data.JsonReader = function(meta, recordType){
16568     
16569     meta = meta || {};
16570     // set some defaults:
16571     Roo.applyIf(meta, {
16572         totalProperty: 'total',
16573         successProperty : 'success',
16574         root : 'data',
16575         id : 'id'
16576     });
16577     
16578     Roo.data.JsonReader.superclass.constructor.call(this, meta, recordType||meta.fields);
16579 };
16580 Roo.extend(Roo.data.JsonReader, Roo.data.DataReader, {
16581     
16582     readerType : 'Json',
16583     
16584     /**
16585      * @prop {Boolean} metaFromRemote  - if the meta data was loaded from the remote source.
16586      * Used by Store query builder to append _requestMeta to params.
16587      * 
16588      */
16589     metaFromRemote : false,
16590     /**
16591      * This method is only used by a DataProxy which has retrieved data from a remote server.
16592      * @param {Object} response The XHR object which contains the JSON data in its responseText.
16593      * @return {Object} data A data block which is used by an Roo.data.Store object as
16594      * a cache of Roo.data.Records.
16595      */
16596     read : function(response){
16597         var json = response.responseText;
16598        
16599         var o = /* eval:var:o */ eval("("+json+")");
16600         if(!o) {
16601             throw {message: "JsonReader.read: Json object not found"};
16602         }
16603         
16604         if(o.metaData){
16605             
16606             delete this.ef;
16607             this.metaFromRemote = true;
16608             this.meta = o.metaData;
16609             this.recordType = Roo.data.Record.create(o.metaData.fields);
16610             this.onMetaChange(this.meta, this.recordType, o);
16611         }
16612         return this.readRecords(o);
16613     },
16614
16615     // private function a store will implement
16616     onMetaChange : function(meta, recordType, o){
16617
16618     },
16619
16620     /**
16621          * @ignore
16622          */
16623     simpleAccess: function(obj, subsc) {
16624         return obj[subsc];
16625     },
16626
16627         /**
16628          * @ignore
16629          */
16630     getJsonAccessor: function(){
16631         var re = /[\[\.]/;
16632         return function(expr) {
16633             try {
16634                 return(re.test(expr))
16635                     ? new Function("obj", "return obj." + expr)
16636                     : function(obj){
16637                         return obj[expr];
16638                     };
16639             } catch(e){}
16640             return Roo.emptyFn;
16641         };
16642     }(),
16643
16644     /**
16645      * Create a data block containing Roo.data.Records from an XML document.
16646      * @param {Object} o An object which contains an Array of row objects in the property specified
16647      * in the config as 'root, and optionally a property, specified in the config as 'totalProperty'
16648      * which contains the total size of the dataset.
16649      * @return {Object} data A data block which is used by an Roo.data.Store object as
16650      * a cache of Roo.data.Records.
16651      */
16652     readRecords : function(o){
16653         /**
16654          * After any data loads, the raw JSON data is available for further custom processing.
16655          * @type Object
16656          */
16657         this.o = o;
16658         var s = this.meta, Record = this.recordType,
16659             f = Record ? Record.prototype.fields : null, fi = f ? f.items : [], fl = f ? f.length : 0;
16660
16661 //      Generate extraction functions for the totalProperty, the root, the id, and for each field
16662         if (!this.ef) {
16663             if(s.totalProperty) {
16664                     this.getTotal = this.getJsonAccessor(s.totalProperty);
16665                 }
16666                 if(s.successProperty) {
16667                     this.getSuccess = this.getJsonAccessor(s.successProperty);
16668                 }
16669                 this.getRoot = s.root ? this.getJsonAccessor(s.root) : function(p){return p;};
16670                 if (s.id) {
16671                         var g = this.getJsonAccessor(s.id);
16672                         this.getId = function(rec) {
16673                                 var r = g(rec);  
16674                                 return (r === undefined || r === "") ? null : r;
16675                         };
16676                 } else {
16677                         this.getId = function(){return null;};
16678                 }
16679             this.ef = [];
16680             for(var jj = 0; jj < fl; jj++){
16681                 f = fi[jj];
16682                 var map = (f.mapping !== undefined && f.mapping !== null) ? f.mapping : f.name;
16683                 this.ef[jj] = this.getJsonAccessor(map);
16684             }
16685         }
16686
16687         var root = this.getRoot(o), c = root.length, totalRecords = c, success = true;
16688         if(s.totalProperty){
16689             var vt = parseInt(this.getTotal(o), 10);
16690             if(!isNaN(vt)){
16691                 totalRecords = vt;
16692             }
16693         }
16694         if(s.successProperty){
16695             var vs = this.getSuccess(o);
16696             if(vs === false || vs === 'false'){
16697                 success = false;
16698             }
16699         }
16700         var records = [];
16701         for(var i = 0; i < c; i++){
16702             var n = root[i];
16703             var values = {};
16704             var id = this.getId(n);
16705             for(var j = 0; j < fl; j++){
16706                 f = fi[j];
16707                                 var v = this.ef[j](n);
16708                                 if (!f.convert) {
16709                                         Roo.log('missing convert for ' + f.name);
16710                                         Roo.log(f);
16711                                         continue;
16712                                 }
16713                                 values[f.name] = f.convert((v !== undefined) ? v : f.defaultValue);
16714             }
16715                         if (!Record) {
16716                                 return {
16717                                         raw : { errorMsg : "JSON Reader Error: fields or metadata not available to create Record" },
16718                                         success : false,
16719                                         records : [],
16720                                         totalRecords : 0
16721                                 };
16722                         }
16723             var record = new Record(values, id);
16724             record.json = n;
16725             records[i] = record;
16726         }
16727         return {
16728             raw : o,
16729             success : success,
16730             records : records,
16731             totalRecords : totalRecords
16732         };
16733     },
16734     // used when loading children.. @see loadDataFromChildren
16735     toLoadData: function(rec)
16736     {
16737         // expect rec just to be an array.. eg [a,b,c, [...] << cn ]
16738         var data = typeof(rec.data.cn) == 'undefined' ? [] : rec.data.cn;
16739         return { data : data, total : data.length };
16740         
16741     }
16742 });/*
16743  * Based on:
16744  * Ext JS Library 1.1.1
16745  * Copyright(c) 2006-2007, Ext JS, LLC.
16746  *
16747  * Originally Released Under LGPL - original licence link has changed is not relivant.
16748  *
16749  * Fork - LGPL
16750  * <script type="text/javascript">
16751  */
16752
16753 /**
16754  * @class Roo.data.ArrayReader
16755  * @extends Roo.data.DataReader
16756  * Data reader class to create an Array of Roo.data.Record objects from an Array.
16757  * Each element of that Array represents a row of data fields. The
16758  * fields are pulled into a Record object using as a subscript, the <em>mapping</em> property
16759  * of the field definition if it exists, or the field's ordinal position in the definition.<br>
16760  * <p>
16761  * Example code:.
16762  * <pre><code>
16763 var RecordDef = Roo.data.Record.create([
16764     {name: 'name', mapping: 1},         // "mapping" only needed if an "id" field is present which
16765     {name: 'occupation', mapping: 2}    // precludes using the ordinal position as the index.
16766 ]);
16767 var myReader = new Roo.data.ArrayReader({
16768     id: 0                     // The subscript within row Array that provides an ID for the Record (optional)
16769 }, RecordDef);
16770 </code></pre>
16771  * <p>
16772  * This would consume an Array like this:
16773  * <pre><code>
16774 [ [1, 'Bill', 'Gardener'], [2, 'Ben', 'Horticulturalist'] ]
16775   </code></pre>
16776  
16777  * @constructor
16778  * Create a new JsonReader
16779  * @param {Object} meta Metadata configuration options.
16780  * @param {Object|Array} recordType Either an Array of field definition objects
16781  * 
16782  * @cfg {Array} fields Array of field definition objects
16783  * @cfg {String} id Name of the property within a row object that contains a record identifier value.
16784  * as specified to {@link Roo.data.Record#create},
16785  * or an {@link Roo.data.Record} object
16786  *
16787  * 
16788  * created using {@link Roo.data.Record#create}.
16789  */
16790 Roo.data.ArrayReader = function(meta, recordType)
16791 {    
16792     Roo.data.ArrayReader.superclass.constructor.call(this, meta, recordType||meta.fields);
16793 };
16794
16795 Roo.extend(Roo.data.ArrayReader, Roo.data.JsonReader, {
16796     
16797       /**
16798      * Create a data block containing Roo.data.Records from an XML document.
16799      * @param {Object} o An Array of row objects which represents the dataset.
16800      * @return {Object} A data block which is used by an {@link Roo.data.Store} object as
16801      * a cache of Roo.data.Records.
16802      */
16803     readRecords : function(o)
16804     {
16805         var sid = this.meta ? this.meta.id : null;
16806         var recordType = this.recordType, fields = recordType.prototype.fields;
16807         var records = [];
16808         var root = o;
16809         for(var i = 0; i < root.length; i++){
16810             var n = root[i];
16811             var values = {};
16812             var id = ((sid || sid === 0) && n[sid] !== undefined && n[sid] !== "" ? n[sid] : null);
16813             for(var j = 0, jlen = fields.length; j < jlen; j++){
16814                 var f = fields.items[j];
16815                 var k = f.mapping !== undefined && f.mapping !== null ? f.mapping : j;
16816                 var v = n[k] !== undefined ? n[k] : f.defaultValue;
16817                 v = f.convert(v);
16818                 values[f.name] = v;
16819             }
16820             var record = new recordType(values, id);
16821             record.json = n;
16822             records[records.length] = record;
16823         }
16824         return {
16825             records : records,
16826             totalRecords : records.length
16827         };
16828     },
16829     // used when loading children.. @see loadDataFromChildren
16830     toLoadData: function(rec)
16831     {
16832         // expect rec just to be an array.. eg [a,b,c, [...] << cn ]
16833         return typeof(rec.data.cn) == 'undefined' ? [] : rec.data.cn;
16834         
16835     }
16836     
16837     
16838 });/*
16839  * - LGPL
16840  * * 
16841  */
16842
16843 /**
16844  * @class Roo.bootstrap.form.ComboBox
16845  * @extends Roo.bootstrap.form.TriggerField
16846  * A combobox control with support for autocomplete, remote-loading, paging and many other features.
16847  * @cfg {Boolean} append (true|false) default false
16848  * @cfg {Boolean} autoFocus (true|false) auto focus the first item, default true
16849  * @cfg {Boolean} tickable ComboBox with tickable selections (true|false), default false
16850  * @cfg {Boolean} triggerList trigger show the list or not (true|false) default true
16851  * @cfg {Boolean} showToggleBtn show toggle button or not (true|false) default true
16852  * @cfg {String} btnPosition set the position of the trigger button (left | right) default right
16853  * @cfg {Boolean} animate default true
16854  * @cfg {Boolean} emptyResultText only for touch device
16855  * @cfg {String} triggerText multiple combobox trigger button text default 'Select'
16856  * @cfg {String} emptyTitle default ''
16857  * @cfg {Number} width fixed with? experimental
16858  * @constructor
16859  * Create a new ComboBox.
16860  * @param {Object} config Configuration options
16861  */
16862 Roo.bootstrap.form.ComboBox = function(config){
16863     Roo.bootstrap.form.ComboBox.superclass.constructor.call(this, config);
16864     this.addEvents({
16865         /**
16866          * @event expand
16867          * Fires when the dropdown list is expanded
16868         * @param {Roo.bootstrap.form.ComboBox} combo This combo box
16869         */
16870         'expand' : true,
16871         /**
16872          * @event collapse
16873          * Fires when the dropdown list is collapsed
16874         * @param {Roo.bootstrap.form.ComboBox} combo This combo box
16875         */
16876         'collapse' : true,
16877         /**
16878          * @event beforeselect
16879          * Fires before a list item is selected. Return false to cancel the selection.
16880         * @param {Roo.bootstrap.form.ComboBox} combo This combo box
16881         * @param {Roo.data.Record} record The data record returned from the underlying store
16882         * @param {Number} index The index of the selected item in the dropdown list
16883         */
16884         'beforeselect' : true,
16885         /**
16886          * @event select
16887          * Fires when a list item is selected
16888         * @param {Roo.bootstrap.form.ComboBox} combo This combo box
16889         * @param {Roo.data.Record} record The data record returned from the underlying store (or false on clear)
16890         * @param {Number} index The index of the selected item in the dropdown list
16891         */
16892         'select' : true,
16893         /**
16894          * @event beforequery
16895          * Fires before all queries are processed. Return false to cancel the query or set cancel to true.
16896          * The event object passed has these properties:
16897         * @param {Roo.bootstrap.form.ComboBox} combo This combo box
16898         * @param {String} query The query
16899         * @param {Boolean} forceAll true to force "all" query
16900         * @param {Boolean} cancel true to cancel the query
16901         * @param {Object} e The query event object
16902         */
16903         'beforequery': true,
16904          /**
16905          * @event add
16906          * Fires when the 'add' icon is pressed (add a listener to enable add button)
16907         * @param {Roo.bootstrap.form.ComboBox} combo This combo box
16908         */
16909         'add' : true,
16910         /**
16911          * @event edit
16912          * Fires when the 'edit' icon is pressed (add a listener to enable add button)
16913         * @param {Roo.bootstrap.form.ComboBox} combo This combo box
16914         * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
16915         */
16916         'edit' : true,
16917         /**
16918          * @event remove
16919          * Fires when the remove value from the combobox array
16920         * @param {Roo.bootstrap.form.ComboBox} combo This combo box
16921         */
16922         'remove' : true,
16923         /**
16924          * @event afterremove
16925          * Fires when the remove value from the combobox array
16926         * @param {Roo.bootstrap.form.ComboBox} combo This combo box
16927         */
16928         'afterremove' : true,
16929         /**
16930          * @event specialfilter
16931          * Fires when specialfilter
16932             * @param {Roo.bootstrap.form.ComboBox} combo This combo box
16933             */
16934         'specialfilter' : true,
16935         /**
16936          * @event tick
16937          * Fires when tick the element
16938             * @param {Roo.bootstrap.form.ComboBox} combo This combo box
16939             */
16940         'tick' : true,
16941         /**
16942          * @event touchviewdisplay
16943          * Fires when touch view require special display (default is using displayField)
16944             * @param {Roo.bootstrap.form.ComboBox} combo This combo box
16945             * @param {Object} cfg set html .
16946             */
16947         'touchviewdisplay' : true
16948         
16949     });
16950     
16951     this.item = [];
16952     this.tickItems = [];
16953     
16954     this.selectedIndex = -1;
16955     if(this.mode == 'local'){
16956         if(config.queryDelay === undefined){
16957             this.queryDelay = 10;
16958         }
16959         if(config.minChars === undefined){
16960             this.minChars = 0;
16961         }
16962     }
16963 };
16964
16965 Roo.extend(Roo.bootstrap.form.ComboBox, Roo.bootstrap.form.TriggerField, {
16966      
16967     /**
16968      * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
16969      * rendering into an Roo.Editor, defaults to false)
16970      */
16971     /**
16972      * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
16973      * {tag: "input", type: "text", size: "24", autocomplete: "off"})
16974      */
16975     /**
16976      * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
16977      */
16978     /**
16979      * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
16980      * the dropdown list (defaults to undefined, with no header element)
16981      */
16982
16983      /**
16984      * @cfg {String/Roo.Template} tpl The template to use to render the output default is  '<a class="dropdown-item" href="#">{' + this.displayField + '}</a>' 
16985      */
16986      
16987      /**
16988      * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
16989      */
16990     listWidth: undefined,
16991     /**
16992      * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
16993      * mode = 'remote' or 'text' if mode = 'local')
16994      */
16995     displayField: undefined,
16996     
16997     /**
16998      * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
16999      * mode = 'remote' or 'value' if mode = 'local'). 
17000      * Note: use of a valueField requires the user make a selection
17001      * in order for a value to be mapped.
17002      */
17003     valueField: undefined,
17004     /**
17005      * @cfg {String} modalTitle The title of the dialog that pops up on mobile views.
17006      */
17007     modalTitle : '',
17008     
17009     /**
17010      * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
17011      * field's data value (defaults to the underlying DOM element's name)
17012      */
17013     hiddenName: undefined,
17014     /**
17015      * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
17016      */
17017     listClass: '',
17018     /**
17019      * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
17020      */
17021     selectedClass: 'active',
17022     
17023     /**
17024      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
17025      */
17026     shadow:'sides',
17027     /**
17028      * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
17029      * anchor positions (defaults to 'tl-bl')
17030      */
17031     listAlign: 'tl-bl?',
17032     /**
17033      * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
17034      */
17035     maxHeight: 300,
17036     /**
17037      * @cfg {String} triggerAction The action to execute when the trigger field is activated.  Use 'all' to run the
17038      * query specified by the allQuery config option (defaults to 'query')
17039      */
17040     triggerAction: 'query',
17041     /**
17042      * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
17043      * (defaults to 4, does not apply if editable = false)
17044      */
17045     minChars : 4,
17046     /**
17047      * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
17048      * delay (typeAheadDelay) if it matches a known value (defaults to false)
17049      */
17050     typeAhead: false,
17051     /**
17052      * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
17053      * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
17054      */
17055     queryDelay: 500,
17056     /**
17057      * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
17058      * filter queries will execute with page start and limit parameters.  Only applies when mode = 'remote' (defaults to 0)
17059      */
17060     pageSize: 0,
17061     /**
17062      * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus.  Only applies
17063      * when editable = true (defaults to false)
17064      */
17065     selectOnFocus:false,
17066     /**
17067      * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
17068      */
17069     queryParam: 'query',
17070     /**
17071      * @cfg {String} loadingText The text to display in the dropdown list while data is loading.  Only applies
17072      * when mode = 'remote' (defaults to 'Loading...')
17073      */
17074     loadingText: 'Loading...',
17075     /**
17076      * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
17077      */
17078     resizable: false,
17079     /**
17080      * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
17081      */
17082     handleHeight : 8,
17083     /**
17084      * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
17085      * traditional select (defaults to true)
17086      */
17087     editable: true,
17088     /**
17089      * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
17090      */
17091     allQuery: '',
17092     /**
17093      * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
17094      */
17095     mode: 'remote',
17096     /**
17097      * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
17098      * listWidth has a higher value)
17099      */
17100     minListWidth : 70,
17101     /**
17102      * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
17103      * allow the user to set arbitrary text into the field (defaults to false)
17104      */
17105     forceSelection:false,
17106     /**
17107      * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
17108      * if typeAhead = true (defaults to 250)
17109      */
17110     typeAheadDelay : 250,
17111     /**
17112      * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
17113      * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
17114      */
17115     valueNotFoundText : undefined,
17116     /**
17117      * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
17118      */
17119     blockFocus : false,
17120     
17121     /**
17122      * @cfg {Boolean} disableClear Disable showing of clear button.
17123      */
17124     disableClear : false,
17125     /**
17126      * @cfg {Boolean} alwaysQuery  Disable caching of results, and always send query
17127      */
17128     alwaysQuery : false,
17129     
17130     /**
17131      * @cfg {Boolean} multiple  (true|false) ComboBobArray, default false
17132      */
17133     multiple : false,
17134     
17135     /**
17136      * @cfg {String} invalidClass DEPRICATED - uses BS4 is-valid now
17137      */
17138     invalidClass : "has-warning",
17139     
17140     /**
17141      * @cfg {String} validClass DEPRICATED - uses BS4 is-valid now
17142      */
17143     validClass : "has-success",
17144     
17145     /**
17146      * @cfg {Boolean} specialFilter (true|false) special filter default false
17147      */
17148     specialFilter : false,
17149     
17150     /**
17151      * @cfg {Boolean} mobileTouchView (true|false) show mobile touch view when using a mobile default true
17152      */
17153     mobileTouchView : true,
17154     
17155     /**
17156      * @cfg {Boolean} useNativeIOS (true|false) render it as classic select for ios, not support dynamic load data (default false)
17157      */
17158     useNativeIOS : false,
17159     
17160     /**
17161      * @cfg {Boolean} mobile_restrict_height (true|false) restrict height for touch view
17162      */
17163     mobile_restrict_height : false,
17164     
17165     ios_options : false,
17166     
17167     //private
17168     addicon : false,
17169     editicon: false,
17170     
17171     page: 0,
17172     hasQuery: false,
17173     append: false,
17174     loadNext: false,
17175     autoFocus : true,
17176     tickable : false,
17177     btnPosition : 'right',
17178     triggerList : true,
17179     showToggleBtn : true,
17180     animate : true,
17181     emptyResultText: 'Empty',
17182     triggerText : 'Select',
17183     emptyTitle : '',
17184     width : false,
17185     
17186     // element that contains real text value.. (when hidden is used..)
17187     
17188     getAutoCreate : function()
17189     {   
17190         var cfg = false;
17191         //render
17192         /*
17193          * Render classic select for iso
17194          */
17195         
17196         if(Roo.isIOS && this.useNativeIOS){
17197             cfg = this.getAutoCreateNativeIOS();
17198             return cfg;
17199         }
17200         
17201         /*
17202          * Touch Devices
17203          */
17204         
17205         if(Roo.isTouch && this.mobileTouchView){
17206             cfg = this.getAutoCreateTouchView();
17207             return cfg;;
17208         }
17209         
17210         /*
17211          *  Normal ComboBox
17212          */
17213         if(!this.tickable){
17214             cfg = Roo.bootstrap.form.ComboBox.superclass.getAutoCreate.call(this);
17215             return cfg;
17216         }
17217         
17218         /*
17219          *  ComboBox with tickable selections
17220          */
17221              
17222         var align = this.labelAlign || this.parentLabelAlign();
17223         
17224         cfg = {
17225             cls : 'form-group roo-combobox-tickable' //input-group
17226         };
17227         
17228         var btn_text_select = '';
17229         var btn_text_done = '';
17230         var btn_text_cancel = '';
17231         
17232         if (this.btn_text_show) {
17233             btn_text_select = 'Select';
17234             btn_text_done = 'Done';
17235             btn_text_cancel = 'Cancel'; 
17236         }
17237         
17238         var buttons = {
17239             tag : 'div',
17240             cls : 'tickable-buttons',
17241             cn : [
17242                 {
17243                     tag : 'button',
17244                     type : 'button',
17245                     cls : 'btn btn-link btn-edit pull-' + this.btnPosition,
17246                     //html : this.triggerText
17247                     html: btn_text_select
17248                 },
17249                 {
17250                     tag : 'button',
17251                     type : 'button',
17252                     name : 'ok',
17253                     cls : 'btn btn-link btn-ok pull-' + this.btnPosition,
17254                     //html : 'Done'
17255                     html: btn_text_done
17256                 },
17257                 {
17258                     tag : 'button',
17259                     type : 'button',
17260                     name : 'cancel',
17261                     cls : 'btn btn-link btn-cancel pull-' + this.btnPosition,
17262                     //html : 'Cancel'
17263                     html: btn_text_cancel
17264                 }
17265             ]
17266         };
17267         
17268         if(this.editable){
17269             buttons.cn.unshift({
17270                 tag: 'input',
17271                 cls: 'roo-select2-search-field-input'
17272             });
17273         }
17274         
17275         var _this = this;
17276         
17277         Roo.each(buttons.cn, function(c){
17278             if (_this.size) {
17279                 c.cls += ' btn-' + _this.size;
17280             }
17281
17282             if (_this.disabled) {
17283                 c.disabled = true;
17284             }
17285         });
17286         
17287         var box = {
17288             tag: 'div',
17289             style : 'display: contents',
17290             cn: [
17291                 {
17292                     tag: 'input',
17293                     type : 'hidden',
17294                     cls: 'form-hidden-field'
17295                 },
17296                 {
17297                     tag: 'ul',
17298                     cls: 'roo-select2-choices',
17299                     cn:[
17300                         {
17301                             tag: 'li',
17302                             cls: 'roo-select2-search-field',
17303                             cn: [
17304                                 buttons
17305                             ]
17306                         }
17307                     ]
17308                 }
17309             ]
17310         };
17311         
17312         var combobox = {
17313             cls: 'roo-select2-container input-group roo-select2-container-multi',
17314             cn: [
17315                 
17316                 box
17317 //                {
17318 //                    tag: 'ul',
17319 //                    cls: 'typeahead typeahead-long dropdown-menu',
17320 //                    style: 'display:none; max-height:' + this.maxHeight + 'px;'
17321 //                }
17322             ]
17323         };
17324         
17325         if(this.hasFeedback && !this.allowBlank){
17326             
17327             var feedback = {
17328                 tag: 'span',
17329                 cls: 'glyphicon form-control-feedback'
17330             };
17331
17332             combobox.cn.push(feedback);
17333         }
17334         
17335         
17336         
17337         var indicator = {
17338             tag : 'i',
17339             cls : 'roo-required-indicator ' + (this.indicatorpos == 'right'  ? 'right' : 'left') +'-indicator text-danger fa fa-lg fa-star',
17340             tooltip : 'This field is required'
17341         };
17342          
17343         if (this.allowBlank) {
17344             indicator = {
17345                 tag : 'i',
17346                 style : 'display:none'
17347             };
17348         } 
17349         if (align ==='left' && this.fieldLabel.length) {
17350             
17351             cfg.cls += ' roo-form-group-label-left'  + (Roo.bootstrap.version == 4 ? ' row' : '');
17352             
17353             cfg.cn = [
17354                 indicator,
17355                 {
17356                     tag: 'label',
17357                     'for' :  id,
17358                     cls : 'control-label col-form-label',
17359                     html : this.fieldLabel
17360
17361                 },
17362                 {
17363                     cls : "", 
17364                     cn: [
17365                         combobox
17366                     ]
17367                 }
17368
17369             ];
17370             
17371             var labelCfg = cfg.cn[1];
17372             var contentCfg = cfg.cn[2];
17373             
17374
17375             if(this.indicatorpos == 'right'){
17376                 
17377                 cfg.cn = [
17378                     {
17379                         tag: 'label',
17380                         'for' :  id,
17381                         cls : 'control-label col-form-label',
17382                         cn : [
17383                             {
17384                                 tag : 'span',
17385                                 html : this.fieldLabel
17386                             },
17387                             indicator
17388                         ]
17389                     },
17390                     {
17391                         cls : "",
17392                         cn: [
17393                             combobox
17394                         ]
17395                     }
17396
17397                 ];
17398                 
17399                 
17400                 
17401                 labelCfg = cfg.cn[0];
17402                 contentCfg = cfg.cn[1];
17403             
17404             }
17405             
17406             if(this.labelWidth > 12){
17407                 labelCfg.style = "width: " + this.labelWidth + 'px';
17408             }
17409             if(this.width * 1 > 0){
17410                 contentCfg.style = "width: " + this.width + 'px';
17411             }
17412             if(this.labelWidth < 13 && this.labelmd == 0){
17413                 this.labelmd = this.labelWidth;
17414             }
17415             
17416             if(this.labellg > 0){
17417                 labelCfg.cls += ' col-lg-' + this.labellg;
17418                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
17419             }
17420             
17421             if(this.labelmd > 0){
17422                 labelCfg.cls += ' col-md-' + this.labelmd;
17423                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
17424             }
17425             
17426             if(this.labelsm > 0){
17427                 labelCfg.cls += ' col-sm-' + this.labelsm;
17428                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
17429             }
17430             
17431             if(this.labelxs > 0){
17432                 labelCfg.cls += ' col-xs-' + this.labelxs;
17433                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
17434             }
17435                 
17436                 
17437         } else if ( this.fieldLabel.length) {
17438 //                Roo.log(" label");
17439                  cfg.cn = [
17440                    indicator,
17441                     {
17442                         tag: 'label',
17443                         //cls : 'input-group-addon',
17444                         html : this.fieldLabel
17445                     },
17446                     combobox
17447                 ];
17448                 
17449                 if(this.indicatorpos == 'right'){
17450                     cfg.cn = [
17451                         {
17452                             tag: 'label',
17453                             //cls : 'input-group-addon',
17454                             html : this.fieldLabel
17455                         },
17456                         indicator,
17457                         combobox
17458                     ];
17459                     
17460                 }
17461
17462         } else {
17463             
17464 //                Roo.log(" no label && no align");
17465                 cfg = combobox
17466                      
17467                 
17468         }
17469          
17470         var settings=this;
17471         ['xs','sm','md','lg'].map(function(size){
17472             if (settings[size]) {
17473                 cfg.cls += ' col-' + size + '-' + settings[size];
17474             }
17475         });
17476         
17477         return cfg;
17478         
17479     },
17480     
17481     _initEventsCalled : false,
17482     
17483     // private
17484     initEvents: function()
17485     {   
17486         if (this._initEventsCalled) { // as we call render... prevent looping...
17487             return;
17488         }
17489         this._initEventsCalled = true;
17490         
17491         if (!this.store) {
17492             throw "can not find store for combo";
17493         }
17494         
17495         this.indicator = this.indicatorEl();
17496         
17497         this.store = Roo.factory(this.store, Roo.data);
17498         this.store.parent = this;
17499         
17500         // if we are building from html. then this element is so complex, that we can not really
17501         // use the rendered HTML.
17502         // so we have to trash and replace the previous code.
17503         if (Roo.XComponent.build_from_html) {
17504             // remove this element....
17505             var e = this.el.dom, k=0;
17506             while (e ) { e = e.previousSibling;  ++k;}
17507
17508             this.el.remove();
17509             
17510             this.el=false;
17511             this.rendered = false;
17512             
17513             this.render(this.parent().getChildContainer(true), k);
17514         }
17515         
17516         if(Roo.isIOS && this.useNativeIOS){
17517             this.initIOSView();
17518             return;
17519         }
17520         
17521         /*
17522          * Touch Devices
17523          */
17524         
17525         if(Roo.isTouch && this.mobileTouchView){
17526             this.initTouchView();
17527             return;
17528         }
17529         
17530         if(this.tickable){
17531             this.initTickableEvents();
17532             return;
17533         }
17534         
17535         Roo.bootstrap.form.ComboBox.superclass.initEvents.call(this);
17536         
17537         if(this.hiddenName){
17538             
17539             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
17540             
17541             this.hiddenField.dom.value =
17542                 this.hiddenValue !== undefined ? this.hiddenValue :
17543                 this.value !== undefined ? this.value : '';
17544
17545             // prevent input submission
17546             this.el.dom.removeAttribute('name');
17547             this.hiddenField.dom.setAttribute('name', this.hiddenName);
17548              
17549              
17550         }
17551         //if(Roo.isGecko){
17552         //    this.el.dom.setAttribute('autocomplete', 'off');
17553         //}
17554         
17555         var cls = 'x-combo-list';
17556         
17557         //this.list = new Roo.Layer({
17558         //    shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false
17559         //});
17560         
17561         var _this = this;
17562         
17563         (function(){
17564             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
17565             _this.list.setWidth(lw);
17566         }).defer(100);
17567         
17568         this.list.on('mouseover', this.onViewOver, this);
17569         this.list.on('mousemove', this.onViewMove, this);
17570         this.list.on('scroll', this.onViewScroll, this);
17571         
17572         /*
17573         this.list.swallowEvent('mousewheel');
17574         this.assetHeight = 0;
17575
17576         if(this.title){
17577             this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
17578             this.assetHeight += this.header.getHeight();
17579         }
17580
17581         this.innerList = this.list.createChild({cls:cls+'-inner'});
17582         this.innerList.on('mouseover', this.onViewOver, this);
17583         this.innerList.on('mousemove', this.onViewMove, this);
17584         this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
17585         
17586         if(this.allowBlank && !this.pageSize && !this.disableClear){
17587             this.footer = this.list.createChild({cls:cls+'-ft'});
17588             this.pageTb = new Roo.Toolbar(this.footer);
17589            
17590         }
17591         if(this.pageSize){
17592             this.footer = this.list.createChild({cls:cls+'-ft'});
17593             this.pageTb = new Roo.PagingToolbar(this.footer, this.store,
17594                     {pageSize: this.pageSize});
17595             
17596         }
17597         
17598         if (this.pageTb && this.allowBlank && !this.disableClear) {
17599             var _this = this;
17600             this.pageTb.add(new Roo.Toolbar.Fill(), {
17601                 cls: 'x-btn-icon x-btn-clear',
17602                 text: '&#160;',
17603                 handler: function()
17604                 {
17605                     _this.collapse();
17606                     _this.clearValue();
17607                     _this.onSelect(false, -1);
17608                 }
17609             });
17610         }
17611         if (this.footer) {
17612             this.assetHeight += this.footer.getHeight();
17613         }
17614         */
17615             
17616         if(!this.tpl){
17617             this.tpl = Roo.bootstrap.version == 4 ?
17618                 '<a class="dropdown-item" href="#">{' + this.displayField + '}</a>' :  // 4 does not need <li> and it get's really confisued.
17619                 '<li><a class="dropdown-item" href="#">{' + this.displayField + '}</a></li>';
17620         }
17621
17622         this.view = new Roo.View(this.list, this.tpl, {
17623             singleSelect:true, store: this.store, selectedClass: this.selectedClass
17624         });
17625         //this.view.wrapEl.setDisplayed(false);
17626         this.view.on('click', this.onViewClick, this);
17627         
17628         
17629         this.store.on('beforeload', this.onBeforeLoad, this);
17630         this.store.on('load', this.onLoad, this);
17631         this.store.on('loadexception', this.onLoadException, this);
17632         /*
17633         if(this.resizable){
17634             this.resizer = new Roo.Resizable(this.list,  {
17635                pinned:true, handles:'se'
17636             });
17637             this.resizer.on('resize', function(r, w, h){
17638                 this.maxHeight = h-this.handleHeight-this.list.getFrameWidth('tb')-this.assetHeight;
17639                 this.listWidth = w;
17640                 this.innerList.setWidth(w - this.list.getFrameWidth('lr'));
17641                 this.restrictHeight();
17642             }, this);
17643             this[this.pageSize?'footer':'innerList'].setStyle('margin-bottom', this.handleHeight+'px');
17644         }
17645         */
17646         if(!this.editable){
17647             this.editable = true;
17648             this.setEditable(false);
17649         }
17650         
17651         /*
17652         
17653         if (typeof(this.events.add.listeners) != 'undefined') {
17654             
17655             this.addicon = this.wrap.createChild(
17656                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-add' });  
17657        
17658             this.addicon.on('click', function(e) {
17659                 this.fireEvent('add', this);
17660             }, this);
17661         }
17662         if (typeof(this.events.edit.listeners) != 'undefined') {
17663             
17664             this.editicon = this.wrap.createChild(
17665                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-edit' });  
17666             if (this.addicon) {
17667                 this.editicon.setStyle('margin-left', '40px');
17668             }
17669             this.editicon.on('click', function(e) {
17670                 
17671                 // we fire even  if inothing is selected..
17672                 this.fireEvent('edit', this, this.lastData );
17673                 
17674             }, this);
17675         }
17676         */
17677         
17678         this.keyNav = new Roo.KeyNav(this.inputEl(), {
17679             "up" : function(e){
17680                 this.inKeyMode = true;
17681                 this.selectPrev();
17682             },
17683
17684             "down" : function(e){
17685                 if(!this.isExpanded()){
17686                     this.onTriggerClick();
17687                 }else{
17688                     this.inKeyMode = true;
17689                     this.selectNext();
17690                 }
17691             },
17692
17693             "enter" : function(e){
17694 //                this.onViewClick();
17695                 //return true;
17696                 this.collapse();
17697                 
17698                 if(this.fireEvent("specialkey", this, e)){
17699                     this.onViewClick(false);
17700                 }
17701                 
17702                 return true;
17703             },
17704
17705             "esc" : function(e){
17706                 this.collapse();
17707             },
17708
17709             "tab" : function(e){
17710                 this.collapse();
17711                 
17712                 if(this.fireEvent("specialkey", this, e)){
17713                     this.onViewClick(false);
17714                 }
17715                 
17716                 return true;
17717             },
17718
17719             scope : this,
17720
17721             doRelay : function(foo, bar, hname){
17722                 if(hname == 'down' || this.scope.isExpanded()){
17723                    return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
17724                 }
17725                 return true;
17726             },
17727
17728             forceKeyDown: true
17729         });
17730         
17731         
17732         this.queryDelay = Math.max(this.queryDelay || 10,
17733                 this.mode == 'local' ? 10 : 250);
17734         
17735         
17736         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
17737         
17738         if(this.typeAhead){
17739             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
17740         }
17741         if(this.editable !== false){
17742             this.inputEl().on("keyup", this.onKeyUp, this);
17743         }
17744         if(this.forceSelection){
17745             this.inputEl().on('blur', this.doForce, this);
17746         }
17747         
17748         if(this.multiple){
17749             this.choices = this.el.select('ul.roo-select2-choices', true).first();
17750             this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
17751         }
17752     },
17753     
17754     initTickableEvents: function()
17755     {   
17756         this.createList();
17757         
17758         if(this.hiddenName){
17759             
17760             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
17761             
17762             this.hiddenField.dom.value =
17763                 this.hiddenValue !== undefined ? this.hiddenValue :
17764                 this.value !== undefined ? this.value : '';
17765
17766             // prevent input submission
17767             this.el.dom.removeAttribute('name');
17768             this.hiddenField.dom.setAttribute('name', this.hiddenName);
17769              
17770              
17771         }
17772         
17773 //        this.list = this.el.select('ul.dropdown-menu',true).first();
17774         
17775         this.choices = this.el.select('ul.roo-select2-choices', true).first();
17776         this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
17777         if(this.triggerList){
17778             this.searchField.on("click", this.onSearchFieldClick, this, {preventDefault:true});
17779         }
17780          
17781         this.trigger = this.el.select('.tickable-buttons > .btn-edit', true).first();
17782         this.trigger.on("click", this.onTickableTriggerClick, this, {preventDefault:true});
17783         
17784         this.okBtn = this.el.select('.tickable-buttons > .btn-ok', true).first();
17785         this.cancelBtn = this.el.select('.tickable-buttons > .btn-cancel', true).first();
17786         
17787         this.okBtn.on('click', this.onTickableFooterButtonClick, this, this.okBtn);
17788         this.cancelBtn.on('click', this.onTickableFooterButtonClick, this, this.cancelBtn);
17789         
17790         this.trigger.setVisibilityMode(Roo.Element.DISPLAY);
17791         this.okBtn.setVisibilityMode(Roo.Element.DISPLAY);
17792         this.cancelBtn.setVisibilityMode(Roo.Element.DISPLAY);
17793         
17794         this.okBtn.hide();
17795         this.cancelBtn.hide();
17796         
17797         var _this = this;
17798         
17799         (function(){
17800             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
17801             _this.list.setWidth(lw);
17802         }).defer(100);
17803         
17804         this.list.on('mouseover', this.onViewOver, this);
17805         this.list.on('mousemove', this.onViewMove, this);
17806         
17807         this.list.on('scroll', this.onViewScroll, this);
17808         
17809         if(!this.tpl){
17810             this.tpl = '<li class="roo-select2-result"><div class="checkbox"><input id="{roo-id}"' + 
17811                 'type="checkbox" {roo-data-checked}><label for="{roo-id}"><b>{' + this.displayField + '}</b></label></div></li>';
17812         }
17813
17814         this.view = new Roo.View(this.list, this.tpl, {
17815             singleSelect:true,
17816             tickable:true,
17817             parent:this,
17818             store: this.store,
17819             selectedClass: this.selectedClass
17820         });
17821         
17822         //this.view.wrapEl.setDisplayed(false);
17823         this.view.on('click', this.onViewClick, this);
17824         
17825         
17826         
17827         this.store.on('beforeload', this.onBeforeLoad, this);
17828         this.store.on('load', this.onLoad, this);
17829         this.store.on('loadexception', this.onLoadException, this);
17830         
17831         if(this.editable){
17832             this.keyNav = new Roo.KeyNav(this.tickableInputEl(), {
17833                 "up" : function(e){
17834                     this.inKeyMode = true;
17835                     this.selectPrev();
17836                 },
17837
17838                 "down" : function(e){
17839                     this.inKeyMode = true;
17840                     this.selectNext();
17841                 },
17842
17843                 "enter" : function(e){
17844                     if(this.fireEvent("specialkey", this, e)){
17845                         this.onViewClick(false);
17846                     }
17847                     
17848                     return true;
17849                 },
17850
17851                 "esc" : function(e){
17852                     this.onTickableFooterButtonClick(e, false, false);
17853                 },
17854
17855                 "tab" : function(e){
17856                     this.fireEvent("specialkey", this, e);
17857                     
17858                     this.onTickableFooterButtonClick(e, false, false);
17859                     
17860                     return true;
17861                 },
17862
17863                 scope : this,
17864
17865                 doRelay : function(e, fn, key){
17866                     if(this.scope.isExpanded()){
17867                        return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
17868                     }
17869                     return true;
17870                 },
17871
17872                 forceKeyDown: true
17873             });
17874         }
17875         
17876         this.queryDelay = Math.max(this.queryDelay || 10,
17877                 this.mode == 'local' ? 10 : 250);
17878         
17879         
17880         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
17881         
17882         if(this.typeAhead){
17883             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
17884         }
17885         
17886         if(this.editable !== false){
17887             this.tickableInputEl().on("keyup", this.onKeyUp, this);
17888         }
17889         
17890         this.indicator = this.indicatorEl();
17891         
17892         if(this.indicator){
17893             this.indicator.setVisibilityMode(Roo.Element.DISPLAY);
17894             this.indicator.hide();
17895         }
17896         
17897     },
17898
17899     onDestroy : function(){
17900         if(this.view){
17901             this.view.setStore(null);
17902             this.view.el.removeAllListeners();
17903             this.view.el.remove();
17904             this.view.purgeListeners();
17905         }
17906         if(this.list){
17907             this.list.dom.innerHTML  = '';
17908         }
17909         
17910         if(this.store){
17911             this.store.un('beforeload', this.onBeforeLoad, this);
17912             this.store.un('load', this.onLoad, this);
17913             this.store.un('loadexception', this.onLoadException, this);
17914         }
17915         Roo.bootstrap.form.ComboBox.superclass.onDestroy.call(this);
17916     },
17917
17918     // private
17919     fireKey : function(e){
17920         if(e.isNavKeyPress() && !this.list.isVisible()){
17921             this.fireEvent("specialkey", this, e);
17922         }
17923     },
17924
17925     // private
17926     onResize: function(w, h)
17927     {
17928         
17929         
17930 //        Roo.bootstrap.form.ComboBox.superclass.onResize.apply(this, arguments);
17931 //        
17932 //        if(typeof w != 'number'){
17933 //            // we do not handle it!?!?
17934 //            return;
17935 //        }
17936 //        var tw = this.trigger.getWidth();
17937 //       // tw += this.addicon ? this.addicon.getWidth() : 0;
17938 //       // tw += this.editicon ? this.editicon.getWidth() : 0;
17939 //        var x = w - tw;
17940 //        this.inputEl().setWidth( this.adjustWidth('input', x));
17941 //            
17942 //        //this.trigger.setStyle('left', x+'px');
17943 //        
17944 //        if(this.list && this.listWidth === undefined){
17945 //            var lw = Math.max(x + this.trigger.getWidth(), this.minListWidth);
17946 //            this.list.setWidth(lw);
17947 //            this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
17948 //        }
17949         
17950     
17951         
17952     },
17953
17954     /**
17955      * Allow or prevent the user from directly editing the field text.  If false is passed,
17956      * the user will only be able to select from the items defined in the dropdown list.  This method
17957      * is the runtime equivalent of setting the 'editable' config option at config time.
17958      * @param {Boolean} value True to allow the user to directly edit the field text
17959      */
17960     setEditable : function(value){
17961         if(value == this.editable){
17962             return;
17963         }
17964         this.editable = value;
17965         if(!value){
17966             this.inputEl().dom.setAttribute('readOnly', true);
17967             this.inputEl().on('mousedown', this.onTriggerClick,  this);
17968             this.inputEl().addClass('x-combo-noedit');
17969         }else{
17970             this.inputEl().dom.removeAttribute('readOnly');
17971             this.inputEl().un('mousedown', this.onTriggerClick,  this);
17972             this.inputEl().removeClass('x-combo-noedit');
17973         }
17974     },
17975
17976     // private
17977     
17978     onBeforeLoad : function(combo,opts){
17979         if(!this.hasFocus){
17980             return;
17981         }
17982          if (!opts.add) {
17983             this.list.dom.innerHTML = '<li class="loading-indicator">'+(this.loadingText||'loading')+'</li>' ;
17984          }
17985         this.restrictHeight();
17986         this.selectedIndex = -1;
17987     },
17988
17989     // private
17990     onLoad : function(){
17991         
17992         this.hasQuery = false;
17993         
17994         if(!this.hasFocus){
17995             return;
17996         }
17997         
17998         if(typeof(this.loading) !== 'undefined' && this.loading !== null){
17999             this.loading.hide();
18000         }
18001         
18002         if(this.store.getCount() > 0){
18003             
18004             this.expand();
18005             this.restrictHeight();
18006             if(this.lastQuery == this.allQuery){
18007                 if(this.editable && !this.tickable){
18008                     this.inputEl().dom.select();
18009                 }
18010                 
18011                 if(
18012                     !this.selectByValue(this.value, true) &&
18013                     this.autoFocus && 
18014                     (
18015                         !this.store.lastOptions ||
18016                         typeof(this.store.lastOptions.add) == 'undefined' || 
18017                         this.store.lastOptions.add != true
18018                     )
18019                 ){
18020                     this.select(0, true);
18021                 }
18022             }else{
18023                 if(this.autoFocus){
18024                     this.selectNext();
18025                 }
18026                 if(this.typeAhead && this.lastKey != Roo.EventObject.BACKSPACE && this.lastKey != Roo.EventObject.DELETE){
18027                     this.taTask.delay(this.typeAheadDelay);
18028                 }
18029             }
18030         }else{
18031             this.onEmptyResults();
18032         }
18033         
18034         //this.el.focus();
18035     },
18036     // private
18037     onLoadException : function()
18038     {
18039         this.hasQuery = false;
18040         
18041         if(typeof(this.loading) !== 'undefined' && this.loading !== null){
18042             this.loading.hide();
18043         }
18044         
18045         if(this.tickable && this.editable){
18046             return;
18047         }
18048         
18049         this.collapse();
18050         // only causes errors at present
18051         //Roo.log(this.store.reader.jsonData);
18052         //if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
18053             // fixme
18054             //Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
18055         //}
18056         
18057         
18058     },
18059     // private
18060     onTypeAhead : function(){
18061         if(this.store.getCount() > 0){
18062             var r = this.store.getAt(0);
18063             var newValue = r.data[this.displayField];
18064             var len = newValue.length;
18065             var selStart = this.getRawValue().length;
18066             
18067             if(selStart != len){
18068                 this.setRawValue(newValue);
18069                 this.selectText(selStart, newValue.length);
18070             }
18071         }
18072     },
18073
18074     // private
18075     onSelect : function(record, index){
18076         
18077         if(this.fireEvent('beforeselect', this, record, index) !== false){
18078         
18079             this.setFromData(index > -1 ? record.data : false);
18080             
18081             this.collapse();
18082             this.fireEvent('select', this, record, index);
18083         }
18084     },
18085
18086     /**
18087      * Returns the currently selected field value or empty string if no value is set.
18088      * @return {String} value The selected value
18089      */
18090     getValue : function()
18091     {
18092         if(Roo.isIOS && this.useNativeIOS){
18093             return this.ios_options[this.inputEl().dom.selectedIndex].data[this.valueField];
18094         }
18095         
18096         if(this.multiple){
18097             return (this.hiddenField) ? this.hiddenField.dom.value : this.value;
18098         }
18099         
18100         if(this.valueField){
18101             return typeof this.value != 'undefined' ? this.value : '';
18102         }else{
18103             return Roo.bootstrap.form.ComboBox.superclass.getValue.call(this);
18104         }
18105     },
18106     
18107     getRawValue : function()
18108     {
18109         if(Roo.isIOS && this.useNativeIOS){
18110             return this.ios_options[this.inputEl().dom.selectedIndex].data[this.displayField];
18111         }
18112         
18113         var v = this.inputEl().getValue();
18114         
18115         return v;
18116     },
18117
18118     /**
18119      * Clears any text/value currently set in the field
18120      */
18121     clearValue : function(){
18122         
18123         if(this.hiddenField){
18124             this.hiddenField.dom.value = '';
18125         }
18126         this.value = '';
18127         this.setRawValue('');
18128         this.lastSelectionText = '';
18129         this.lastData = false;
18130         
18131         var close = this.closeTriggerEl();
18132         
18133         if(close){
18134             close.hide();
18135         }
18136         
18137         this.validate();
18138         
18139     },
18140
18141     /**
18142      * Sets the specified value into the field.  If the value finds a match, the corresponding record text
18143      * will be displayed in the field.  If the value does not match the data value of an existing item,
18144      * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
18145      * Otherwise the field will be blank (although the value will still be set).
18146      * @param {String} value The value to match
18147      */
18148     setValue : function(v)
18149     {
18150         if(Roo.isIOS && this.useNativeIOS){
18151             this.setIOSValue(v);
18152             return;
18153         }
18154         
18155         if(this.multiple){
18156             this.syncValue();
18157             return;
18158         }
18159         
18160         var text = v;
18161         if(this.valueField){
18162             var r = this.findRecord(this.valueField, v);
18163             if(r){
18164                 text = r.data[this.displayField];
18165             }else if(this.valueNotFoundText !== undefined){
18166                 text = this.valueNotFoundText;
18167             }
18168         }
18169         this.lastSelectionText = text;
18170         if(this.hiddenField){
18171             this.hiddenField.dom.value = v;
18172         }
18173         Roo.bootstrap.form.ComboBox.superclass.setValue.call(this, text);
18174         this.value = v;
18175         
18176         var close = this.closeTriggerEl();
18177         
18178         if(close){
18179             (v && (v.length || v * 1 > 0)) ? close.show() : close.hide();
18180         }
18181         
18182         this.validate();
18183     },
18184     /**
18185      * @property {Object} the last set data for the element
18186      */
18187     
18188     lastData : false,
18189     /**
18190      * Sets the value of the field based on a object which is related to the record format for the store.
18191      * @param {Object} value the value to set as. or false on reset?
18192      */
18193     setFromData : function(o){
18194         
18195         if(this.multiple){
18196             this.addItem(o);
18197             return;
18198         }
18199             
18200         var dv = ''; // display value
18201         var vv = ''; // value value..
18202         this.lastData = o;
18203         if (this.displayField) {
18204             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
18205         } else {
18206             // this is an error condition!!!
18207             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
18208         }
18209         
18210         if(this.valueField){
18211             vv = !o || typeof(o[this.valueField]) == 'undefined' ? dv : o[this.valueField];
18212         }
18213         
18214         var close = this.closeTriggerEl();
18215         
18216         if(close){
18217             if(dv.length || vv * 1 > 0){
18218                 close.show() ;
18219                 this.blockFocus=true;
18220             } else {
18221                 close.hide();
18222             }             
18223         }
18224         
18225         if(this.hiddenField){
18226             this.hiddenField.dom.value = vv;
18227             
18228             this.lastSelectionText = dv;
18229             Roo.bootstrap.form.ComboBox.superclass.setValue.call(this, dv);
18230             this.value = vv;
18231             return;
18232         }
18233         // no hidden field.. - we store the value in 'value', but still display
18234         // display field!!!!
18235         this.lastSelectionText = dv;
18236         Roo.bootstrap.form.ComboBox.superclass.setValue.call(this, dv);
18237         this.value = vv;
18238         
18239         
18240         
18241     },
18242     // private
18243     reset : function(){
18244         // overridden so that last data is reset..
18245         
18246         if(this.multiple){
18247             this.clearItem();
18248             return;
18249         }
18250         
18251         this.setValue(this.originalValue);
18252         //this.clearInvalid();
18253         this.lastData = false;
18254         if (this.view) {
18255             this.view.clearSelections();
18256         }
18257         
18258         this.validate();
18259     },
18260     // private
18261     findRecord : function(prop, value){
18262         var record;
18263         if(this.store.getCount() > 0){
18264             this.store.each(function(r){
18265                 if(r.data[prop] == value){
18266                     record = r;
18267                     return false;
18268                 }
18269                 return true;
18270             });
18271         }
18272         return record;
18273     },
18274     
18275     getName: function()
18276     {
18277         // returns hidden if it's set..
18278         if (!this.rendered) {return ''};
18279         return !this.hiddenName && this.inputEl().dom.name  ? this.inputEl().dom.name : (this.hiddenName || '');
18280         
18281     },
18282     // private
18283     onViewMove : function(e, t){
18284         this.inKeyMode = false;
18285     },
18286
18287     // private
18288     onViewOver : function(e, t){
18289         if(this.inKeyMode){ // prevent key nav and mouse over conflicts
18290             return;
18291         }
18292         var item = this.view.findItemFromChild(t);
18293         
18294         if(item){
18295             var index = this.view.indexOf(item);
18296             this.select(index, false);
18297         }
18298     },
18299
18300     // private
18301     onViewClick : function(view, doFocus, el, e)
18302     {
18303         var index = this.view.getSelectedIndexes()[0];
18304         
18305         var r = this.store.getAt(index);
18306         
18307         if(this.tickable){
18308             
18309             if(typeof(e) != 'undefined' && e.getTarget().nodeName.toLowerCase() != 'input'){
18310                 return;
18311             }
18312             
18313             var rm = false;
18314             var _this = this;
18315             
18316             Roo.each(this.tickItems, function(v,k){
18317                 
18318                 if(typeof(v) != 'undefined' && v[_this.valueField] == r.data[_this.valueField]){
18319                     Roo.log(v);
18320                     _this.tickItems.splice(k, 1);
18321                     
18322                     if(typeof(e) == 'undefined' && view == false){
18323                         Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = false;
18324                     }
18325                     
18326                     rm = true;
18327                     return;
18328                 }
18329             });
18330             
18331             if(rm){
18332                 return;
18333             }
18334             
18335             if(this.fireEvent('tick', this, r, index, Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked) !== false){
18336                 this.tickItems.push(r.data);
18337             }
18338             
18339             if(typeof(e) == 'undefined' && view == false){
18340                 Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = true;
18341             }
18342                     
18343             return;
18344         }
18345         
18346         if(r){
18347             this.onSelect(r, index);
18348         }
18349         if(doFocus !== false && !this.blockFocus){
18350             this.inputEl().focus();
18351         }
18352     },
18353
18354     // private
18355     restrictHeight : function(){
18356         //this.innerList.dom.style.height = '';
18357         //var inner = this.innerList.dom;
18358         //var h = Math.max(inner.clientHeight, inner.offsetHeight, inner.scrollHeight);
18359         //this.innerList.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
18360         //this.list.beginUpdate();
18361         //this.list.setHeight(this.innerList.getHeight()+this.list.getFrameWidth('tb')+(this.resizable?this.handleHeight:0)+this.assetHeight);
18362         this.list.alignTo(this.inputEl(), this.listAlign);
18363         this.list.alignTo(this.inputEl(), this.listAlign);
18364         //this.list.endUpdate();
18365     },
18366
18367     // private
18368     onEmptyResults : function(){
18369         
18370         if(this.tickable && this.editable){
18371             this.hasFocus = false;
18372             this.restrictHeight();
18373             return;
18374         }
18375         
18376         this.collapse();
18377     },
18378
18379     /**
18380      * Returns true if the dropdown list is expanded, else false.
18381      */
18382     isExpanded : function(){
18383         return this.list.isVisible();
18384     },
18385
18386     /**
18387      * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
18388      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
18389      * @param {String} value The data value of the item to select
18390      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
18391      * selected item if it is not currently in view (defaults to true)
18392      * @return {Boolean} True if the value matched an item in the list, else false
18393      */
18394     selectByValue : function(v, scrollIntoView){
18395         if(v !== undefined && v !== null){
18396             var r = this.findRecord(this.valueField || this.displayField, v);
18397             if(r){
18398                 this.select(this.store.indexOf(r), scrollIntoView);
18399                 return true;
18400             }
18401         }
18402         return false;
18403     },
18404
18405     /**
18406      * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
18407      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
18408      * @param {Number} index The zero-based index of the list item to select
18409      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
18410      * selected item if it is not currently in view (defaults to true)
18411      */
18412     select : function(index, scrollIntoView){
18413         this.selectedIndex = index;
18414         this.view.select(index);
18415         if(scrollIntoView !== false){
18416             var el = this.view.getNode(index);
18417             /*
18418              * el && !this.multiple && !this.tickable // not sure why we disable multiple before..
18419              */
18420             if(el){
18421                 this.list.scrollChildIntoView(el, false);
18422             }
18423         }
18424     },
18425
18426     // private
18427     selectNext : function(){
18428         var ct = this.store.getCount();
18429         if(ct > 0){
18430             if(this.selectedIndex == -1){
18431                 this.select(0);
18432             }else if(this.selectedIndex < ct-1){
18433                 this.select(this.selectedIndex+1);
18434             }
18435         }
18436     },
18437
18438     // private
18439     selectPrev : function(){
18440         var ct = this.store.getCount();
18441         if(ct > 0){
18442             if(this.selectedIndex == -1){
18443                 this.select(0);
18444             }else if(this.selectedIndex != 0){
18445                 this.select(this.selectedIndex-1);
18446             }
18447         }
18448     },
18449
18450     // private
18451     onKeyUp : function(e){
18452         if(this.editable !== false && !e.isSpecialKey()){
18453             this.lastKey = e.getKey();
18454             this.dqTask.delay(this.queryDelay);
18455         }
18456     },
18457
18458     // private
18459     validateBlur : function(){
18460         return !this.list || !this.list.isVisible();   
18461     },
18462
18463     // private
18464     initQuery : function(){
18465         
18466         var v = this.getRawValue();
18467         
18468         if(this.tickable && this.editable){
18469             v = this.tickableInputEl().getValue();
18470         }
18471         
18472         this.doQuery(v);
18473     },
18474
18475     // private
18476     doForce : function(){
18477         if(this.inputEl().dom.value.length > 0){
18478             this.inputEl().dom.value =
18479                 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
18480              
18481         }
18482     },
18483
18484     /**
18485      * Execute a query to filter the dropdown list.  Fires the beforequery event prior to performing the
18486      * query allowing the query action to be canceled if needed.
18487      * @param {String} query The SQL query to execute
18488      * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
18489      * in the field than the minimum specified by the minChars config option.  It also clears any filter previously
18490      * saved in the current store (defaults to false)
18491      */
18492     doQuery : function(q, forceAll){
18493         
18494         if(q === undefined || q === null){
18495             q = '';
18496         }
18497         var qe = {
18498             query: q,
18499             forceAll: forceAll,
18500             combo: this,
18501             cancel:false
18502         };
18503         if(this.fireEvent('beforequery', qe)===false || qe.cancel){
18504             return false;
18505         }
18506         q = qe.query;
18507         
18508         forceAll = qe.forceAll;
18509         if(forceAll === true || (q.length >= this.minChars)){
18510             
18511             this.hasQuery = true;
18512             
18513             if(this.lastQuery != q || this.alwaysQuery){
18514                 this.lastQuery = q;
18515                 if(this.mode == 'local'){
18516                     this.selectedIndex = -1;
18517                     if(forceAll){
18518                         this.store.clearFilter();
18519                     }else{
18520                         
18521                         if(this.specialFilter){
18522                             this.fireEvent('specialfilter', this);
18523                             this.onLoad();
18524                             return;
18525                         }
18526                         
18527                         this.store.filter(this.displayField, q);
18528                     }
18529                     
18530                     this.store.fireEvent("datachanged", this.store);
18531                     
18532                     this.onLoad();
18533                     
18534                     
18535                 }else{
18536                     
18537                     this.store.baseParams[this.queryParam] = q;
18538                     
18539                     var options = {params : this.getParams(q)};
18540                     
18541                     if(this.loadNext){
18542                         options.add = true;
18543                         options.params.start = this.page * this.pageSize;
18544                     }
18545                     
18546                     this.store.load(options);
18547                     
18548                     /*
18549                      *  this code will make the page width larger, at the beginning, the list not align correctly, 
18550                      *  we should expand the list on onLoad
18551                      *  so command out it
18552                      */
18553 //                    this.expand();
18554                 }
18555             }else{
18556                 this.selectedIndex = -1;
18557                 this.onLoad();   
18558             }
18559         }
18560         
18561         this.loadNext = false;
18562     },
18563     
18564     // private
18565     getParams : function(q){
18566         var p = {};
18567         //p[this.queryParam] = q;
18568         
18569         if(this.pageSize){
18570             p.start = 0;
18571             p.limit = this.pageSize;
18572         }
18573         return p;
18574     },
18575
18576     /**
18577      * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
18578      */
18579     collapse : function(){
18580         if(!this.isExpanded()){
18581             return;
18582         }
18583         
18584         this.list.hide();
18585         
18586         this.hasFocus = false;
18587         
18588         if(this.tickable){
18589             this.okBtn.hide();
18590             this.cancelBtn.hide();
18591             this.trigger.show();
18592             
18593             if(this.editable){
18594                 this.tickableInputEl().dom.value = '';
18595                 this.tickableInputEl().blur();
18596             }
18597             
18598         }
18599         
18600         Roo.get(document).un('mousedown', this.collapseIf, this);
18601         Roo.get(document).un('mousewheel', this.collapseIf, this);
18602         if (!this.editable) {
18603             Roo.get(document).un('keydown', this.listKeyPress, this);
18604         }
18605         this.fireEvent('collapse', this);
18606         
18607         this.validate();
18608     },
18609
18610     // private
18611     collapseIf : function(e){
18612         var in_combo  = e.within(this.el);
18613         var in_list =  e.within(this.list);
18614         var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
18615         
18616         if (in_combo || in_list || is_list) {
18617             //e.stopPropagation();
18618             return;
18619         }
18620         
18621         if(this.tickable){
18622             this.onTickableFooterButtonClick(e, false, false);
18623         }
18624
18625         this.collapse();
18626         
18627     },
18628
18629     /**
18630      * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
18631      */
18632     expand : function(){
18633        
18634         if(this.isExpanded() || !this.hasFocus){
18635             return;
18636         }
18637         
18638         var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
18639         this.list.setWidth(lw);
18640         
18641         Roo.log('expand');
18642         
18643         this.list.show();
18644         
18645         this.restrictHeight();
18646         
18647         if(this.tickable){
18648             
18649             this.tickItems = Roo.apply([], this.item);
18650             
18651             this.okBtn.show();
18652             this.cancelBtn.show();
18653             this.trigger.hide();
18654             
18655             if(this.editable){
18656                 this.tickableInputEl().focus();
18657             }
18658             
18659         }
18660         
18661         Roo.get(document).on('mousedown', this.collapseIf, this);
18662         Roo.get(document).on('mousewheel', this.collapseIf, this);
18663         if (!this.editable) {
18664             Roo.get(document).on('keydown', this.listKeyPress, this);
18665         }
18666         
18667         this.fireEvent('expand', this);
18668     },
18669
18670     // private
18671     // Implements the default empty TriggerField.onTriggerClick function
18672     onTriggerClick : function(e)
18673     {
18674         Roo.log('trigger click');
18675         
18676         if(this.disabled || !this.triggerList){
18677             return;
18678         }
18679         
18680         this.page = 0;
18681         this.loadNext = false;
18682         
18683         if(this.isExpanded()){
18684             this.collapse();
18685             if (!this.blockFocus) {
18686                 this.inputEl().focus();
18687             }
18688             
18689         }else {
18690             this.hasFocus = true;
18691             if(this.triggerAction == 'all') {
18692                 this.doQuery(this.allQuery, true);
18693             } else {
18694                 this.doQuery(this.getRawValue());
18695             }
18696             if (!this.blockFocus) {
18697                 this.inputEl().focus();
18698             }
18699         }
18700     },
18701     
18702     onTickableTriggerClick : function(e)
18703     {
18704         if(this.disabled){
18705             return;
18706         }
18707         
18708         this.page = 0;
18709         this.loadNext = false;
18710         this.hasFocus = true;
18711         
18712         if(this.triggerAction == 'all') {
18713             this.doQuery(this.allQuery, true);
18714         } else {
18715             this.doQuery(this.getRawValue());
18716         }
18717     },
18718     
18719     onSearchFieldClick : function(e)
18720     {
18721         if(this.hasFocus && !this.disabled && e.getTarget().nodeName.toLowerCase() != 'button'){
18722             this.onTickableFooterButtonClick(e, false, false);
18723             return;
18724         }
18725         
18726         if(this.hasFocus || this.disabled || e.getTarget().nodeName.toLowerCase() == 'button'){
18727             return;
18728         }
18729         
18730         this.page = 0;
18731         this.loadNext = false;
18732         this.hasFocus = true;
18733         
18734         if(this.triggerAction == 'all') {
18735             this.doQuery(this.allQuery, true);
18736         } else {
18737             this.doQuery(this.getRawValue());
18738         }
18739     },
18740     
18741     listKeyPress : function(e)
18742     {
18743         //Roo.log('listkeypress');
18744         // scroll to first matching element based on key pres..
18745         if (e.isSpecialKey()) {
18746             return false;
18747         }
18748         var k = String.fromCharCode(e.getKey()).toUpperCase();
18749         //Roo.log(k);
18750         var match  = false;
18751         var csel = this.view.getSelectedNodes();
18752         var cselitem = false;
18753         if (csel.length) {
18754             var ix = this.view.indexOf(csel[0]);
18755             cselitem  = this.store.getAt(ix);
18756             if (!cselitem.get(this.displayField) || cselitem.get(this.displayField).substring(0,1).toUpperCase() != k) {
18757                 cselitem = false;
18758             }
18759             
18760         }
18761         
18762         this.store.each(function(v) { 
18763             if (cselitem) {
18764                 // start at existing selection.
18765                 if (cselitem.id == v.id) {
18766                     cselitem = false;
18767                 }
18768                 return true;
18769             }
18770                 
18771             if (v.get(this.displayField) && v.get(this.displayField).substring(0,1).toUpperCase() == k) {
18772                 match = this.store.indexOf(v);
18773                 return false;
18774             }
18775             return true;
18776         }, this);
18777         
18778         if (match === false) {
18779             return true; // no more action?
18780         }
18781         // scroll to?
18782         this.view.select(match);
18783         var sn = Roo.get(this.view.getSelectedNodes()[0]);
18784         sn.scrollIntoView(sn.dom.parentNode, false);
18785     },
18786     
18787     onViewScroll : function(e, t){
18788         
18789         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){
18790             return;
18791         }
18792         
18793         this.hasQuery = true;
18794         
18795         this.loading = this.list.select('.loading', true).first();
18796         
18797         if(this.loading === null){
18798             this.list.createChild({
18799                 tag: 'div',
18800                 cls: 'loading roo-select2-more-results roo-select2-active',
18801                 html: 'Loading more results...'
18802             });
18803             
18804             this.loading = this.list.select('.loading', true).first();
18805             
18806             this.loading.setVisibilityMode(Roo.Element.DISPLAY);
18807             
18808             this.loading.hide();
18809         }
18810         
18811         this.loading.show();
18812         
18813         var _combo = this;
18814         
18815         this.page++;
18816         this.loadNext = true;
18817         
18818         (function() { _combo.doQuery(_combo.allQuery, true); }).defer(500);
18819         
18820         return;
18821     },
18822     
18823     addItem : function(o)
18824     {   
18825         var dv = ''; // display value
18826         
18827         if (this.displayField) {
18828             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
18829         } else {
18830             // this is an error condition!!!
18831             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
18832         }
18833         
18834         if(!dv.length){
18835             return;
18836         }
18837         
18838         var choice = this.choices.createChild({
18839             tag: 'li',
18840             cls: 'roo-select2-search-choice',
18841             cn: [
18842                 {
18843                     tag: 'div',
18844                     html: dv
18845                 },
18846                 {
18847                     tag: 'a',
18848                     href: '#',
18849                     cls: 'roo-select2-search-choice-close fa fa-times',
18850                     tabindex: '-1'
18851                 }
18852             ]
18853             
18854         }, this.searchField);
18855         
18856         var close = choice.select('a.roo-select2-search-choice-close', true).first();
18857         
18858         close.on('click', this.onRemoveItem, this, { item : choice, data : o} );
18859         
18860         this.item.push(o);
18861         
18862         this.lastData = o;
18863         
18864         this.syncValue();
18865         
18866         this.inputEl().dom.value = '';
18867         
18868         this.validate();
18869     },
18870     
18871     onRemoveItem : function(e, _self, o)
18872     {
18873         e.preventDefault();
18874         
18875         this.lastItem = Roo.apply([], this.item);
18876         
18877         var index = this.item.indexOf(o.data) * 1;
18878         
18879         if( index < 0){
18880             Roo.log('not this item?!');
18881             return;
18882         }
18883         
18884         this.item.splice(index, 1);
18885         o.item.remove();
18886         
18887         this.syncValue();
18888         
18889         this.fireEvent('remove', this, e);
18890         
18891         this.validate();
18892         
18893     },
18894     
18895     syncValue : function()
18896     {
18897         if(!this.item.length){
18898             this.clearValue();
18899             return;
18900         }
18901             
18902         var value = [];
18903         var _this = this;
18904         Roo.each(this.item, function(i){
18905             if(_this.valueField){
18906                 value.push(i[_this.valueField]);
18907                 return;
18908             }
18909
18910             value.push(i);
18911         });
18912
18913         this.value = value.join(',');
18914
18915         if(this.hiddenField){
18916             this.hiddenField.dom.value = this.value;
18917         }
18918         
18919         this.store.fireEvent("datachanged", this.store);
18920         
18921         this.validate();
18922     },
18923     
18924     clearItem : function()
18925     {
18926         if(!this.multiple){
18927             return;
18928         }
18929         
18930         this.item = [];
18931         
18932         Roo.each(this.choices.select('>li.roo-select2-search-choice', true).elements, function(c){
18933            c.remove();
18934         });
18935         
18936         this.syncValue();
18937         
18938         this.validate();
18939         
18940         if(this.tickable && !Roo.isTouch){
18941             this.view.refresh();
18942         }
18943     },
18944     
18945     inputEl: function ()
18946     {
18947         if(Roo.isIOS && this.useNativeIOS){
18948             return this.el.select('select.roo-ios-select', true).first();
18949         }
18950         
18951         if(Roo.isTouch && this.mobileTouchView){
18952             return this.el.select('input.form-control',true).first();
18953         }
18954         
18955         if(this.tickable){
18956             return this.searchField;
18957         }
18958         
18959         return this.el.select('input.form-control',true).first();
18960     },
18961     
18962     onTickableFooterButtonClick : function(e, btn, el)
18963     {
18964         e.preventDefault();
18965         
18966         this.lastItem = Roo.apply([], this.item);
18967         
18968         if(btn && btn.name == 'cancel'){
18969             this.tickItems = Roo.apply([], this.item);
18970             this.collapse();
18971             return;
18972         }
18973         
18974         this.clearItem();
18975         
18976         var _this = this;
18977         
18978         Roo.each(this.tickItems, function(o){
18979             _this.addItem(o);
18980         });
18981         
18982         this.collapse();
18983         
18984     },
18985     
18986     validate : function()
18987     {
18988         if(this.getVisibilityEl().hasClass('hidden')){
18989             return true;
18990         }
18991         
18992         var v = this.getRawValue();
18993         
18994         if(this.multiple){
18995             v = this.getValue();
18996         }
18997         
18998         if(this.disabled || this.allowBlank || v.length){
18999             this.markValid();
19000             return true;
19001         }
19002         
19003         this.markInvalid();
19004         return false;
19005     },
19006     
19007     tickableInputEl : function()
19008     {
19009         if(!this.tickable || !this.editable){
19010             return this.inputEl();
19011         }
19012         
19013         return this.inputEl().select('.roo-select2-search-field-input', true).first();
19014     },
19015     
19016     
19017     getAutoCreateTouchView : function()
19018     {
19019         var id = Roo.id();
19020         
19021         var cfg = {
19022             cls: 'form-group' //input-group
19023         };
19024         
19025         var input =  {
19026             tag: 'input',
19027             id : id,
19028             type : this.inputType,
19029             cls : 'form-control x-combo-noedit',
19030             autocomplete: 'new-password',
19031             placeholder : this.placeholder || '',
19032             readonly : true
19033         };
19034         
19035         if (this.name) {
19036             input.name = this.name;
19037         }
19038         
19039         if (this.size) {
19040             input.cls += ' input-' + this.size;
19041         }
19042         
19043         if (this.disabled) {
19044             input.disabled = true;
19045         }
19046         
19047         var inputblock = {
19048             cls : 'roo-combobox-wrap',
19049             cn : [
19050                 input
19051             ]
19052         };
19053         
19054         if(this.before){
19055             inputblock.cls += ' input-group';
19056             
19057             inputblock.cn.unshift({
19058                 tag :'span',
19059                 cls : 'input-group-addon input-group-prepend input-group-text',
19060                 html : this.before
19061             });
19062         }
19063         
19064         if(this.removable && !this.multiple){
19065             inputblock.cls += ' roo-removable';
19066             
19067             inputblock.cn.push({
19068                 tag: 'button',
19069                 html : 'x',
19070                 cls : 'roo-combo-removable-btn close'
19071             });
19072         }
19073
19074         if(this.hasFeedback && !this.allowBlank){
19075             
19076             inputblock.cls += ' has-feedback';
19077             
19078             inputblock.cn.push({
19079                 tag: 'span',
19080                 cls: 'glyphicon form-control-feedback'
19081             });
19082             
19083         }
19084         
19085         if (this.after) {
19086             
19087             inputblock.cls += (this.before) ? '' : ' input-group';
19088             
19089             inputblock.cn.push({
19090                 tag :'span',
19091                 cls : 'input-group-addon input-group-append input-group-text',
19092                 html : this.after
19093             });
19094         }
19095
19096         
19097         var ibwrap = inputblock;
19098         
19099         if(this.multiple){
19100             ibwrap = {
19101                 tag: 'ul',
19102                 cls: 'roo-select2-choices',
19103                 cn:[
19104                     {
19105                         tag: 'li',
19106                         cls: 'roo-select2-search-field',
19107                         cn: [
19108
19109                             inputblock
19110                         ]
19111                     }
19112                 ]
19113             };
19114         
19115             
19116         }
19117         
19118         var combobox = {
19119             cls: 'roo-select2-container input-group roo-touchview-combobox ',
19120             cn: [
19121                 {
19122                     tag: 'input',
19123                     type : 'hidden',
19124                     cls: 'form-hidden-field'
19125                 },
19126                 ibwrap
19127             ]
19128         };
19129         
19130         if(!this.multiple && this.showToggleBtn){
19131             
19132             var caret = {
19133                 cls: 'caret'
19134             };
19135             
19136             if (this.caret != false) {
19137                 caret = {
19138                      tag: 'i',
19139                      cls: 'fa fa-' + this.caret
19140                 };
19141                 
19142             }
19143             
19144             combobox.cn.push({
19145                 tag :'span',
19146                 cls : 'input-group-addon input-group-append input-group-text btn dropdown-toggle',
19147                 cn : [
19148                     Roo.bootstrap.version == 3 ? caret : '',
19149                     {
19150                         tag: 'span',
19151                         cls: 'combobox-clear',
19152                         cn  : [
19153                             {
19154                                 tag : 'i',
19155                                 cls: 'icon-remove'
19156                             }
19157                         ]
19158                     }
19159                 ]
19160
19161             })
19162         }
19163         
19164         if(this.multiple){
19165             combobox.cls += ' roo-select2-container-multi';
19166         }
19167         
19168         var required =  this.allowBlank ?  {
19169                     tag : 'i',
19170                     style: 'display: none'
19171                 } : {
19172                    tag : 'i',
19173                    cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
19174                    tooltip : 'This field is required'
19175                 };
19176         
19177         var align = this.labelAlign || this.parentLabelAlign();
19178         
19179         if (align ==='left' && this.fieldLabel.length) {
19180
19181             cfg.cn = [
19182                 required,
19183                 {
19184                     tag: 'label',
19185                     cls : 'control-label col-form-label',
19186                     html : this.fieldLabel
19187
19188                 },
19189                 {
19190                     cls : 'roo-combobox-wrap ', 
19191                     cn: [
19192                         combobox
19193                     ]
19194                 }
19195             ];
19196             
19197             var labelCfg = cfg.cn[1];
19198             var contentCfg = cfg.cn[2];
19199             
19200
19201             if(this.indicatorpos == 'right'){
19202                 cfg.cn = [
19203                     {
19204                         tag: 'label',
19205                         'for' :  id,
19206                         cls : 'control-label col-form-label',
19207                         cn : [
19208                             {
19209                                 tag : 'span',
19210                                 html : this.fieldLabel
19211                             },
19212                             required
19213                         ]
19214                     },
19215                     {
19216                         cls : "roo-combobox-wrap ",
19217                         cn: [
19218                             combobox
19219                         ]
19220                     }
19221
19222                 ];
19223                 
19224                 labelCfg = cfg.cn[0];
19225                 contentCfg = cfg.cn[1];
19226             }
19227             
19228            
19229             
19230             if(this.labelWidth > 12){
19231                 labelCfg.style = "width: " + this.labelWidth + 'px';
19232             }
19233            
19234             if(this.labelWidth < 13 && this.labelmd == 0){
19235                 this.labelmd = this.labelWidth;
19236             }
19237             
19238             if(this.labellg > 0){
19239                 labelCfg.cls += ' col-lg-' + this.labellg;
19240                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
19241             }
19242             
19243             if(this.labelmd > 0){
19244                 labelCfg.cls += ' col-md-' + this.labelmd;
19245                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
19246             }
19247             
19248             if(this.labelsm > 0){
19249                 labelCfg.cls += ' col-sm-' + this.labelsm;
19250                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
19251             }
19252             
19253             if(this.labelxs > 0){
19254                 labelCfg.cls += ' col-xs-' + this.labelxs;
19255                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
19256             }
19257                 
19258                 
19259         } else if ( this.fieldLabel.length) {
19260             cfg.cn = [
19261                required,
19262                 {
19263                     tag: 'label',
19264                     cls : 'control-label',
19265                     html : this.fieldLabel
19266
19267                 },
19268                 {
19269                     cls : '', 
19270                     cn: [
19271                         combobox
19272                     ]
19273                 }
19274             ];
19275             
19276             if(this.indicatorpos == 'right'){
19277                 cfg.cn = [
19278                     {
19279                         tag: 'label',
19280                         cls : 'control-label',
19281                         html : this.fieldLabel,
19282                         cn : [
19283                             required
19284                         ]
19285                     },
19286                     {
19287                         cls : '', 
19288                         cn: [
19289                             combobox
19290                         ]
19291                     }
19292                 ];
19293             }
19294         } else {
19295             cfg.cn = combobox;    
19296         }
19297         
19298         
19299         var settings = this;
19300         
19301         ['xs','sm','md','lg'].map(function(size){
19302             if (settings[size]) {
19303                 cfg.cls += ' col-' + size + '-' + settings[size];
19304             }
19305         });
19306         
19307         return cfg;
19308     },
19309     
19310     initTouchView : function()
19311     {
19312         this.renderTouchView();
19313         
19314         this.touchViewEl.on('scroll', function(){
19315             this.el.dom.scrollTop = 0;
19316         }, this);
19317         
19318         this.originalValue = this.getValue();
19319         
19320         this.triggerEl = this.el.select('span.dropdown-toggle',true).first();
19321         
19322         this.inputEl().on("click", this.showTouchView, this);
19323         if (this.triggerEl) {
19324             this.triggerEl.on("click", this.showTouchView, this);
19325         }
19326         
19327         
19328         this.touchViewFooterEl.select('.roo-touch-view-cancel', true).first().on('click', this.hideTouchView, this);
19329         this.touchViewFooterEl.select('.roo-touch-view-ok', true).first().on('click', this.setTouchViewValue, this);
19330         
19331         this.maskEl = new Roo.LoadMask(this.touchViewEl, { store : this.store, msgCls: 'roo-el-mask-msg' });
19332         
19333         this.store.on('beforeload', this.onTouchViewBeforeLoad, this);
19334         this.store.on('load', this.onTouchViewLoad, this);
19335         this.store.on('loadexception', this.onTouchViewLoadException, this);
19336         
19337         if(this.hiddenName){
19338             
19339             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
19340             
19341             this.hiddenField.dom.value =
19342                 this.hiddenValue !== undefined ? this.hiddenValue :
19343                 this.value !== undefined ? this.value : '';
19344         
19345             this.el.dom.removeAttribute('name');
19346             this.hiddenField.dom.setAttribute('name', this.hiddenName);
19347         }
19348         
19349         if(this.multiple){
19350             this.choices = this.el.select('ul.roo-select2-choices', true).first();
19351             this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
19352         }
19353         
19354         if(this.removable && !this.multiple){
19355             var close = this.closeTriggerEl();
19356             if(close){
19357                 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
19358                 close.on('click', this.removeBtnClick, this, close);
19359             }
19360         }
19361         /*
19362          * fix the bug in Safari iOS8
19363          */
19364         this.inputEl().on("focus", function(e){
19365             document.activeElement.blur();
19366         }, this);
19367         
19368         this._touchViewMask = Roo.DomHelper.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
19369         
19370         return;
19371         
19372         
19373     },
19374     
19375     renderTouchView : function()
19376     {
19377         this.touchViewEl = Roo.get(document.body).createChild(Roo.bootstrap.form.ComboBox.touchViewTemplate);
19378         this.touchViewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
19379         
19380         this.touchViewHeaderEl = this.touchViewEl.select('.modal-header', true).first();
19381         this.touchViewHeaderEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
19382         
19383         this.touchViewBodyEl = this.touchViewEl.select('.modal-body', true).first();
19384         this.touchViewBodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
19385         this.touchViewBodyEl.setStyle('overflow', 'auto');
19386         
19387         this.touchViewListGroup = this.touchViewBodyEl.select('.list-group', true).first();
19388         this.touchViewListGroup.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
19389         
19390         this.touchViewFooterEl = this.touchViewEl.select('.modal-footer', true).first();
19391         this.touchViewFooterEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
19392         
19393     },
19394     
19395     showTouchView : function()
19396     {
19397         if(this.disabled){
19398             return;
19399         }
19400         
19401         this.touchViewHeaderEl.hide();
19402
19403         if(this.modalTitle.length){
19404             this.touchViewHeaderEl.dom.innerHTML = this.modalTitle;
19405             this.touchViewHeaderEl.show();
19406         }
19407
19408         this.touchViewEl.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
19409         this.touchViewEl.show();
19410
19411         this.touchViewEl.select('.modal-dialog', true).first().setStyle({ margin : '0px', width : '100%'});
19412         
19413         //this.touchViewEl.select('.modal-dialog > .modal-content', true).first().setSize(
19414         //        Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
19415
19416         var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
19417
19418         if(this.modalTitle.length){
19419             bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
19420         }
19421         
19422         this.touchViewBodyEl.setHeight(bodyHeight);
19423
19424         if(this.animate){
19425             var _this = this;
19426             (function(){ _this.touchViewEl.addClass(['in','show']); }).defer(50);
19427         }else{
19428             this.touchViewEl.addClass(['in','show']);
19429         }
19430         
19431         if(this._touchViewMask){
19432             Roo.get(document.body).addClass("x-body-masked");
19433             this._touchViewMask.setSize(Roo.lib.Dom.getViewWidth(true),   Roo.lib.Dom.getViewHeight(true));
19434             this._touchViewMask.setStyle('z-index', 10000);
19435             this._touchViewMask.addClass('show');
19436         }
19437         
19438         this.doTouchViewQuery();
19439         
19440     },
19441     
19442     hideTouchView : function()
19443     {
19444         this.touchViewEl.removeClass(['in','show']);
19445
19446         if(this.animate){
19447             var _this = this;
19448             (function(){ _this.touchViewEl.setStyle('display', 'none'); }).defer(150);
19449         }else{
19450             this.touchViewEl.setStyle('display', 'none');
19451         }
19452         
19453         if(this._touchViewMask){
19454             this._touchViewMask.removeClass('show');
19455             Roo.get(document.body).removeClass("x-body-masked");
19456         }
19457     },
19458     
19459     setTouchViewValue : function()
19460     {
19461         if(this.multiple){
19462             this.clearItem();
19463         
19464             var _this = this;
19465
19466             Roo.each(this.tickItems, function(o){
19467                 this.addItem(o);
19468             }, this);
19469         }
19470         
19471         this.hideTouchView();
19472     },
19473     
19474     doTouchViewQuery : function()
19475     {
19476         var qe = {
19477             query: '',
19478             forceAll: true,
19479             combo: this,
19480             cancel:false
19481         };
19482         
19483         if(this.fireEvent('beforequery', qe) ===false || qe.cancel){
19484             return false;
19485         }
19486         
19487         if(!this.alwaysQuery || this.mode == 'local'){
19488             this.onTouchViewLoad();
19489             return;
19490         }
19491         
19492         this.store.load();
19493     },
19494     
19495     onTouchViewBeforeLoad : function(combo,opts)
19496     {
19497         return;
19498     },
19499
19500     // private
19501     onTouchViewLoad : function()
19502     {
19503         if(this.store.getCount() < 1){
19504             this.onTouchViewEmptyResults();
19505             return;
19506         }
19507         
19508         this.clearTouchView();
19509         
19510         var rawValue = this.getRawValue();
19511         
19512         var template = (this.multiple) ? Roo.bootstrap.form.ComboBox.listItemCheckbox : Roo.bootstrap.form.ComboBox.listItemRadio;
19513         
19514         this.tickItems = [];
19515         
19516         this.store.data.each(function(d, rowIndex){
19517             var row = this.touchViewListGroup.createChild(template);
19518             
19519             if(typeof(d.data.cls) != 'undefined' && d.data.cls.length){
19520                 row.addClass(d.data.cls);
19521             }
19522             
19523             if(this.displayField && typeof(d.data[this.displayField]) != 'undefined'){
19524                 var cfg = {
19525                     data : d.data,
19526                     html : d.data[this.displayField]
19527                 };
19528                 
19529                 if(this.fireEvent('touchviewdisplay', this, cfg) !== false){
19530                     row.select('.roo-combobox-list-group-item-value', true).first().dom.innerHTML = cfg.html;
19531                 }
19532             }
19533             row.removeClass('selected');
19534             if(!this.multiple && this.valueField &&
19535                     typeof(d.data[this.valueField]) != 'undefined' && d.data[this.valueField] == this.getValue())
19536             {
19537                 // radio buttons..
19538                 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
19539                 row.addClass('selected');
19540             }
19541             
19542             if(this.multiple && this.valueField &&
19543                     typeof(d.data[this.valueField]) != 'undefined' && this.getValue().indexOf(d.data[this.valueField]) != -1)
19544             {
19545                 
19546                 // checkboxes...
19547                 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
19548                 this.tickItems.push(d.data);
19549             }
19550             
19551             row.on('click', this.onTouchViewClick, this, {row : row, rowIndex : rowIndex});
19552             
19553         }, this);
19554         
19555         var firstChecked = this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).first();
19556         
19557         var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
19558
19559         if(this.modalTitle.length){
19560             bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
19561         }
19562
19563         var listHeight = this.touchViewListGroup.getHeight() + this.touchViewBodyEl.getPadding('tb') * 2;
19564         
19565         if(this.mobile_restrict_height && listHeight < bodyHeight){
19566             this.touchViewBodyEl.setHeight(listHeight);
19567         }
19568         
19569         var _this = this;
19570         
19571         if(firstChecked && listHeight > bodyHeight){
19572             (function() { firstChecked.findParent('li').scrollIntoView(_this.touchViewListGroup.dom); }).defer(500);
19573         }
19574         
19575     },
19576     
19577     onTouchViewLoadException : function()
19578     {
19579         this.hideTouchView();
19580     },
19581     
19582     onTouchViewEmptyResults : function()
19583     {
19584         this.clearTouchView();
19585         
19586         this.touchViewListGroup.createChild(Roo.bootstrap.form.ComboBox.emptyResult);
19587         
19588         this.touchViewListGroup.select('.roo-combobox-touch-view-empty-result', true).first().dom.innerHTML = this.emptyResultText;
19589         
19590     },
19591     
19592     clearTouchView : function()
19593     {
19594         this.touchViewListGroup.dom.innerHTML = '';
19595     },
19596     
19597     onTouchViewClick : function(e, el, o)
19598     {
19599         e.preventDefault();
19600         
19601         var row = o.row;
19602         var rowIndex = o.rowIndex;
19603         
19604         var r = this.store.getAt(rowIndex);
19605         
19606         if(this.fireEvent('beforeselect', this, r, rowIndex) !== false){
19607             
19608             if(!this.multiple){
19609                 Roo.each(this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).elements, function(c){
19610                     c.dom.removeAttribute('checked');
19611                 }, this);
19612
19613                 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
19614
19615                 this.setFromData(r.data);
19616
19617                 var close = this.closeTriggerEl();
19618
19619                 if(close){
19620                     close.show();
19621                 }
19622
19623                 this.hideTouchView();
19624
19625                 this.fireEvent('select', this, r, rowIndex);
19626
19627                 return;
19628             }
19629
19630             if(this.valueField && typeof(r.data[this.valueField]) != 'undefined' && this.getValue().indexOf(r.data[this.valueField]) != -1){
19631                 row.select('.roo-combobox-list-group-item-box > input', true).first().dom.removeAttribute('checked');
19632                 this.tickItems.splice(this.tickItems.indexOf(r.data), 1);
19633                 return;
19634             }
19635
19636             row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
19637             this.addItem(r.data);
19638             this.tickItems.push(r.data);
19639         }
19640     },
19641     
19642     getAutoCreateNativeIOS : function()
19643     {
19644         var cfg = {
19645             cls: 'form-group' //input-group,
19646         };
19647         
19648         var combobox =  {
19649             tag: 'select',
19650             cls : 'roo-ios-select'
19651         };
19652         
19653         if (this.name) {
19654             combobox.name = this.name;
19655         }
19656         
19657         if (this.disabled) {
19658             combobox.disabled = true;
19659         }
19660         
19661         var settings = this;
19662         
19663         ['xs','sm','md','lg'].map(function(size){
19664             if (settings[size]) {
19665                 cfg.cls += ' col-' + size + '-' + settings[size];
19666             }
19667         });
19668         
19669         cfg.cn = combobox;
19670         
19671         return cfg;
19672         
19673     },
19674     
19675     initIOSView : function()
19676     {
19677         this.store.on('load', this.onIOSViewLoad, this);
19678         
19679         return;
19680     },
19681     
19682     onIOSViewLoad : function()
19683     {
19684         if(this.store.getCount() < 1){
19685             return;
19686         }
19687         
19688         this.clearIOSView();
19689         
19690         if(this.allowBlank) {
19691             
19692             var default_text = '-- SELECT --';
19693             
19694             if(this.placeholder.length){
19695                 default_text = this.placeholder;
19696             }
19697             
19698             if(this.emptyTitle.length){
19699                 default_text += ' - ' + this.emptyTitle + ' -';
19700             }
19701             
19702             var opt = this.inputEl().createChild({
19703                 tag: 'option',
19704                 value : 0,
19705                 html : default_text
19706             });
19707             
19708             var o = {};
19709             o[this.valueField] = 0;
19710             o[this.displayField] = default_text;
19711             
19712             this.ios_options.push({
19713                 data : o,
19714                 el : opt
19715             });
19716             
19717         }
19718         
19719         this.store.data.each(function(d, rowIndex){
19720             
19721             var html = '';
19722             
19723             if(this.displayField && typeof(d.data[this.displayField]) != 'undefined'){
19724                 html = d.data[this.displayField];
19725             }
19726             
19727             var value = '';
19728             
19729             if(this.valueField && typeof(d.data[this.valueField]) != 'undefined'){
19730                 value = d.data[this.valueField];
19731             }
19732             
19733             var option = {
19734                 tag: 'option',
19735                 value : value,
19736                 html : html
19737             };
19738             
19739             if(this.value == d.data[this.valueField]){
19740                 option['selected'] = true;
19741             }
19742             
19743             var opt = this.inputEl().createChild(option);
19744             
19745             this.ios_options.push({
19746                 data : d.data,
19747                 el : opt
19748             });
19749             
19750         }, this);
19751         
19752         this.inputEl().on('change', function(){
19753            this.fireEvent('select', this);
19754         }, this);
19755         
19756     },
19757     
19758     clearIOSView: function()
19759     {
19760         this.inputEl().dom.innerHTML = '';
19761         
19762         this.ios_options = [];
19763     },
19764     
19765     setIOSValue: function(v)
19766     {
19767         this.value = v;
19768         
19769         if(!this.ios_options){
19770             return;
19771         }
19772         
19773         Roo.each(this.ios_options, function(opts){
19774            
19775            opts.el.dom.removeAttribute('selected');
19776            
19777            if(opts.data[this.valueField] != v){
19778                return;
19779            }
19780            
19781            opts.el.dom.setAttribute('selected', true);
19782            
19783         }, this);
19784     }
19785
19786     /** 
19787     * @cfg {Boolean} grow 
19788     * @hide 
19789     */
19790     /** 
19791     * @cfg {Number} growMin 
19792     * @hide 
19793     */
19794     /** 
19795     * @cfg {Number} growMax 
19796     * @hide 
19797     */
19798     /**
19799      * @hide
19800      * @method autoSize
19801      */
19802 });
19803
19804 Roo.apply(Roo.bootstrap.form.ComboBox,  {
19805     
19806     header : {
19807         tag: 'div',
19808         cls: 'modal-header',
19809         cn: [
19810             {
19811                 tag: 'h4',
19812                 cls: 'modal-title'
19813             }
19814         ]
19815     },
19816     
19817     body : {
19818         tag: 'div',
19819         cls: 'modal-body',
19820         cn: [
19821             {
19822                 tag: 'ul',
19823                 cls: 'list-group'
19824             }
19825         ]
19826     },
19827     
19828     listItemRadio : {
19829         tag: 'li',
19830         cls: 'list-group-item',
19831         cn: [
19832             {
19833                 tag: 'span',
19834                 cls: 'roo-combobox-list-group-item-value'
19835             },
19836             {
19837                 tag: 'div',
19838                 cls: 'roo-combobox-list-group-item-box pull-xs-right radio-inline radio radio-info',
19839                 cn: [
19840                     {
19841                         tag: 'input',
19842                         type: 'radio'
19843                     },
19844                     {
19845                         tag: 'label'
19846                     }
19847                 ]
19848             }
19849         ]
19850     },
19851     
19852     listItemCheckbox : {
19853         tag: 'li',
19854         cls: 'list-group-item',
19855         cn: [
19856             {
19857                 tag: 'span',
19858                 cls: 'roo-combobox-list-group-item-value'
19859             },
19860             {
19861                 tag: 'div',
19862                 cls: 'roo-combobox-list-group-item-box pull-xs-right checkbox-inline checkbox checkbox-info',
19863                 cn: [
19864                     {
19865                         tag: 'input',
19866                         type: 'checkbox'
19867                     },
19868                     {
19869                         tag: 'label'
19870                     }
19871                 ]
19872             }
19873         ]
19874     },
19875     
19876     emptyResult : {
19877         tag: 'div',
19878         cls: 'alert alert-danger roo-combobox-touch-view-empty-result'
19879     },
19880     
19881     footer : {
19882         tag: 'div',
19883         cls: 'modal-footer',
19884         cn: [
19885             {
19886                 tag: 'div',
19887                 cls: 'row',
19888                 cn: [
19889                     {
19890                         tag: 'div',
19891                         cls: 'col-xs-6 text-left',
19892                         cn: {
19893                             tag: 'button',
19894                             cls: 'btn btn-danger roo-touch-view-cancel',
19895                             html: 'Cancel'
19896                         }
19897                     },
19898                     {
19899                         tag: 'div',
19900                         cls: 'col-xs-6 text-right',
19901                         cn: {
19902                             tag: 'button',
19903                             cls: 'btn btn-success roo-touch-view-ok',
19904                             html: 'OK'
19905                         }
19906                     }
19907                 ]
19908             }
19909         ]
19910         
19911     }
19912 });
19913
19914 Roo.apply(Roo.bootstrap.form.ComboBox,  {
19915     
19916     touchViewTemplate : {
19917         tag: 'div',
19918         cls: 'modal fade roo-combobox-touch-view',
19919         cn: [
19920             {
19921                 tag: 'div',
19922                 cls: 'modal-dialog',
19923                 style : 'position:fixed', // we have to fix position....
19924                 cn: [
19925                     {
19926                         tag: 'div',
19927                         cls: 'modal-content',
19928                         cn: [
19929                             Roo.bootstrap.form.ComboBox.header,
19930                             Roo.bootstrap.form.ComboBox.body,
19931                             Roo.bootstrap.form.ComboBox.footer
19932                         ]
19933                     }
19934                 ]
19935             }
19936         ]
19937     }
19938 });/*
19939  * Based on:
19940  * Ext JS Library 1.1.1
19941  * Copyright(c) 2006-2007, Ext JS, LLC.
19942  *
19943  * Originally Released Under LGPL - original licence link has changed is not relivant.
19944  *
19945  * Fork - LGPL
19946  * <script type="text/javascript">
19947  */
19948
19949 /**
19950  * @class Roo.View
19951  * @extends Roo.util.Observable
19952  * Create a "View" for an element based on a data model or UpdateManager and the supplied DomHelper template. 
19953  * This class also supports single and multi selection modes. <br>
19954  * Create a data model bound view:
19955  <pre><code>
19956  var store = new Roo.data.Store(...);
19957
19958  var view = new Roo.View({
19959     el : "my-element",
19960     tpl : '&lt;div id="{0}"&gt;{2} - {1}&lt;/div&gt;', // auto create template
19961  
19962     singleSelect: true,
19963     selectedClass: "ydataview-selected",
19964     store: store
19965  });
19966
19967  // listen for node click?
19968  view.on("click", function(vw, index, node, e){
19969  alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
19970  });
19971
19972  // load XML data
19973  dataModel.load("foobar.xml");
19974  </code></pre>
19975  For an example of creating a JSON/UpdateManager view, see {@link Roo.JsonView}.
19976  * <br><br>
19977  * <b>Note: The root of your template must be a single node. Table/row implementations may work but are not supported due to
19978  * IE"s limited insertion support with tables and Opera"s faulty event bubbling.</b>
19979  * 
19980  * Note: old style constructor is still suported (container, template, config)
19981  * 
19982  * @constructor
19983  * Create a new View
19984  * @param {Object} config The config object
19985  * 
19986  */
19987 Roo.View = function(config, depreciated_tpl, depreciated_config){
19988     
19989     this.parent = false;
19990     
19991     if (typeof(depreciated_tpl) == 'undefined') {
19992         // new way.. - universal constructor.
19993         Roo.apply(this, config);
19994         this.el  = Roo.get(this.el);
19995     } else {
19996         // old format..
19997         this.el  = Roo.get(config);
19998         this.tpl = depreciated_tpl;
19999         Roo.apply(this, depreciated_config);
20000     }
20001     this.wrapEl  = this.el.wrap().wrap();
20002     ///this.el = this.wrapEla.appendChild(document.createElement("div"));
20003     
20004     
20005     if(typeof(this.tpl) == "string"){
20006         this.tpl = new Roo.Template(this.tpl);
20007     } else {
20008         // support xtype ctors..
20009         this.tpl = new Roo.factory(this.tpl, Roo);
20010     }
20011     
20012     
20013     this.tpl.compile();
20014     
20015     /** @private */
20016     this.addEvents({
20017         /**
20018          * @event beforeclick
20019          * Fires before a click is processed. Returns false to cancel the default action.
20020          * @param {Roo.View} this
20021          * @param {Number} index The index of the target node
20022          * @param {HTMLElement} node The target node
20023          * @param {Roo.EventObject} e The raw event object
20024          */
20025             "beforeclick" : true,
20026         /**
20027          * @event click
20028          * Fires when a template node is clicked.
20029          * @param {Roo.View} this
20030          * @param {Number} index The index of the target node
20031          * @param {HTMLElement} node The target node
20032          * @param {Roo.EventObject} e The raw event object
20033          */
20034             "click" : true,
20035         /**
20036          * @event dblclick
20037          * Fires when a template node is double clicked.
20038          * @param {Roo.View} this
20039          * @param {Number} index The index of the target node
20040          * @param {HTMLElement} node The target node
20041          * @param {Roo.EventObject} e The raw event object
20042          */
20043             "dblclick" : true,
20044         /**
20045          * @event contextmenu
20046          * Fires when a template node is right clicked.
20047          * @param {Roo.View} this
20048          * @param {Number} index The index of the target node
20049          * @param {HTMLElement} node The target node
20050          * @param {Roo.EventObject} e The raw event object
20051          */
20052             "contextmenu" : true,
20053         /**
20054          * @event selectionchange
20055          * Fires when the selected nodes change.
20056          * @param {Roo.View} this
20057          * @param {Array} selections Array of the selected nodes
20058          */
20059             "selectionchange" : true,
20060     
20061         /**
20062          * @event beforeselect
20063          * Fires before a selection is made. If any handlers return false, the selection is cancelled.
20064          * @param {Roo.View} this
20065          * @param {HTMLElement} node The node to be selected
20066          * @param {Array} selections Array of currently selected nodes
20067          */
20068             "beforeselect" : true,
20069         /**
20070          * @event preparedata
20071          * Fires on every row to render, to allow you to change the data.
20072          * @param {Roo.View} this
20073          * @param {Object} data to be rendered (change this)
20074          */
20075           "preparedata" : true
20076           
20077           
20078         });
20079
20080
20081
20082     this.el.on({
20083         "click": this.onClick,
20084         "dblclick": this.onDblClick,
20085         "contextmenu": this.onContextMenu,
20086         scope:this
20087     });
20088
20089     this.selections = [];
20090     this.nodes = [];
20091     this.cmp = new Roo.CompositeElementLite([]);
20092     if(this.store){
20093         this.store = Roo.factory(this.store, Roo.data);
20094         this.setStore(this.store, true);
20095     }
20096     
20097     if ( this.footer && this.footer.xtype) {
20098            
20099          var fctr = this.wrapEl.appendChild(document.createElement("div"));
20100         
20101         this.footer.dataSource = this.store;
20102         this.footer.container = fctr;
20103         this.footer = Roo.factory(this.footer, Roo);
20104         fctr.insertFirst(this.el);
20105         
20106         // this is a bit insane - as the paging toolbar seems to detach the el..
20107 //        dom.parentNode.parentNode.parentNode
20108          // they get detached?
20109     }
20110     
20111     
20112     Roo.View.superclass.constructor.call(this);
20113     
20114     
20115 };
20116
20117 Roo.extend(Roo.View, Roo.util.Observable, {
20118     
20119      /**
20120      * @cfg {Roo.data.Store} store Data store to load data from.
20121      */
20122     store : false,
20123     
20124     /**
20125      * @cfg {String|Roo.Element} el The container element.
20126      */
20127     el : '',
20128     
20129     /**
20130      * @cfg {String|Roo.Template} tpl The template used by this View 
20131      */
20132     tpl : false,
20133     /**
20134      * @cfg {String} dataName the named area of the template to use as the data area
20135      *                          Works with domtemplates roo-name="name"
20136      */
20137     dataName: false,
20138     /**
20139      * @cfg {String} selectedClass The css class to add to selected nodes
20140      */
20141     selectedClass : "x-view-selected",
20142      /**
20143      * @cfg {String} emptyText The empty text to show when nothing is loaded.
20144      */
20145     emptyText : "",
20146     
20147     /**
20148      * @cfg {String} text to display on mask (default Loading)
20149      */
20150     mask : false,
20151     /**
20152      * @cfg {Boolean} multiSelect Allow multiple selection
20153      */
20154     multiSelect : false,
20155     /**
20156      * @cfg {Boolean} singleSelect Allow single selection
20157      */
20158     singleSelect:  false,
20159     
20160     /**
20161      * @cfg {Boolean} toggleSelect - selecting 
20162      */
20163     toggleSelect : false,
20164     
20165     /**
20166      * @cfg {Boolean} tickable - selecting 
20167      */
20168     tickable : false,
20169     
20170     /**
20171      * Returns the element this view is bound to.
20172      * @return {Roo.Element}
20173      */
20174     getEl : function(){
20175         return this.wrapEl;
20176     },
20177     
20178     
20179
20180     /**
20181      * Refreshes the view. - called by datachanged on the store. - do not call directly.
20182      */
20183     refresh : function(){
20184         //Roo.log('refresh');
20185         var t = this.tpl;
20186         
20187         // if we are using something like 'domtemplate', then
20188         // the what gets used is:
20189         // t.applySubtemplate(NAME, data, wrapping data..)
20190         // the outer template then get' applied with
20191         //     the store 'extra data'
20192         // and the body get's added to the
20193         //      roo-name="data" node?
20194         //      <span class='roo-tpl-{name}'></span> ?????
20195         
20196         
20197         
20198         this.clearSelections();
20199         this.el.update("");
20200         var html = [];
20201         var records = this.store.getRange();
20202         if(records.length < 1) {
20203             
20204             // is this valid??  = should it render a template??
20205             
20206             this.el.update(this.emptyText);
20207             return;
20208         }
20209         var el = this.el;
20210         if (this.dataName) {
20211             this.el.update(t.apply(this.store.meta)); //????
20212             el = this.el.child('.roo-tpl-' + this.dataName);
20213         }
20214         
20215         for(var i = 0, len = records.length; i < len; i++){
20216             var data = this.prepareData(records[i].data, i, records[i]);
20217             this.fireEvent("preparedata", this, data, i, records[i]);
20218             
20219             var d = Roo.apply({}, data);
20220             
20221             if(this.tickable){
20222                 Roo.apply(d, {'roo-id' : Roo.id()});
20223                 
20224                 var _this = this;
20225             
20226                 Roo.each(this.parent.item, function(item){
20227                     if(item[_this.parent.valueField] != data[_this.parent.valueField]){
20228                         return;
20229                     }
20230                     Roo.apply(d, {'roo-data-checked' : 'checked'});
20231                 });
20232             }
20233             
20234             html[html.length] = Roo.util.Format.trim(
20235                 this.dataName ?
20236                     t.applySubtemplate(this.dataName, d, this.store.meta) :
20237                     t.apply(d)
20238             );
20239         }
20240         
20241         
20242         
20243         el.update(html.join(""));
20244         this.nodes = el.dom.childNodes;
20245         this.updateIndexes(0);
20246     },
20247     
20248
20249     /**
20250      * Function to override to reformat the data that is sent to
20251      * the template for each node.
20252      * DEPRICATED - use the preparedata event handler.
20253      * @param {Array/Object} data The raw data (array of colData for a data model bound view or
20254      * a JSON object for an UpdateManager bound view).
20255      */
20256     prepareData : function(data, index, record)
20257     {
20258         this.fireEvent("preparedata", this, data, index, record);
20259         return data;
20260     },
20261
20262     onUpdate : function(ds, record){
20263         // Roo.log('on update');   
20264         this.clearSelections();
20265         var index = this.store.indexOf(record);
20266         var n = this.nodes[index];
20267         this.tpl.insertBefore(n, this.prepareData(record.data, index, record));
20268         n.parentNode.removeChild(n);
20269         this.updateIndexes(index, index);
20270     },
20271
20272     
20273     
20274 // --------- FIXME     
20275     onAdd : function(ds, records, index)
20276     {
20277         //Roo.log(['on Add', ds, records, index] );        
20278         this.clearSelections();
20279         if(this.nodes.length == 0){
20280             this.refresh();
20281             return;
20282         }
20283         var n = this.nodes[index];
20284         for(var i = 0, len = records.length; i < len; i++){
20285             var d = this.prepareData(records[i].data, i, records[i]);
20286             if(n){
20287                 this.tpl.insertBefore(n, d);
20288             }else{
20289                 
20290                 this.tpl.append(this.el, d);
20291             }
20292         }
20293         this.updateIndexes(index);
20294     },
20295
20296     onRemove : function(ds, record, index){
20297        // Roo.log('onRemove');
20298         this.clearSelections();
20299         var el = this.dataName  ?
20300             this.el.child('.roo-tpl-' + this.dataName) :
20301             this.el; 
20302         
20303         el.dom.removeChild(this.nodes[index]);
20304         this.updateIndexes(index);
20305     },
20306
20307     /**
20308      * Refresh an individual node.
20309      * @param {Number} index
20310      */
20311     refreshNode : function(index){
20312         this.onUpdate(this.store, this.store.getAt(index));
20313     },
20314
20315     updateIndexes : function(startIndex, endIndex){
20316         var ns = this.nodes;
20317         startIndex = startIndex || 0;
20318         endIndex = endIndex || ns.length - 1;
20319         for(var i = startIndex; i <= endIndex; i++){
20320             ns[i].nodeIndex = i;
20321         }
20322     },
20323
20324     /**
20325      * Changes the data store this view uses and refresh the view.
20326      * @param {Store} store
20327      */
20328     setStore : function(store, initial){
20329         if(!initial && this.store){
20330             this.store.un("datachanged", this.refresh);
20331             this.store.un("add", this.onAdd);
20332             this.store.un("remove", this.onRemove);
20333             this.store.un("update", this.onUpdate);
20334             this.store.un("clear", this.refresh);
20335             this.store.un("beforeload", this.onBeforeLoad);
20336             this.store.un("load", this.onLoad);
20337             this.store.un("loadexception", this.onLoad);
20338         }
20339         if(store){
20340           
20341             store.on("datachanged", this.refresh, this);
20342             store.on("add", this.onAdd, this);
20343             store.on("remove", this.onRemove, this);
20344             store.on("update", this.onUpdate, this);
20345             store.on("clear", this.refresh, this);
20346             store.on("beforeload", this.onBeforeLoad, this);
20347             store.on("load", this.onLoad, this);
20348             store.on("loadexception", this.onLoad, this);
20349         }
20350         
20351         if(store){
20352             this.refresh();
20353         }
20354     },
20355     /**
20356      * onbeforeLoad - masks the loading area.
20357      *
20358      */
20359     onBeforeLoad : function(store,opts)
20360     {
20361          //Roo.log('onBeforeLoad');   
20362         if (!opts.add) {
20363             this.el.update("");
20364         }
20365         this.el.mask(this.mask ? this.mask : "Loading" ); 
20366     },
20367     onLoad : function ()
20368     {
20369         this.el.unmask();
20370     },
20371     
20372
20373     /**
20374      * Returns the template node the passed child belongs to or null if it doesn't belong to one.
20375      * @param {HTMLElement} node
20376      * @return {HTMLElement} The template node
20377      */
20378     findItemFromChild : function(node){
20379         var el = this.dataName  ?
20380             this.el.child('.roo-tpl-' + this.dataName,true) :
20381             this.el.dom; 
20382         
20383         if(!node || node.parentNode == el){
20384                     return node;
20385             }
20386             var p = node.parentNode;
20387             while(p && p != el){
20388             if(p.parentNode == el){
20389                 return p;
20390             }
20391             p = p.parentNode;
20392         }
20393             return null;
20394     },
20395
20396     /** @ignore */
20397     onClick : function(e){
20398         var item = this.findItemFromChild(e.getTarget());
20399         if(item){
20400             var index = this.indexOf(item);
20401             if(this.onItemClick(item, index, e) !== false){
20402                 this.fireEvent("click", this, index, item, e);
20403             }
20404         }else{
20405             this.clearSelections();
20406         }
20407     },
20408
20409     /** @ignore */
20410     onContextMenu : function(e){
20411         var item = this.findItemFromChild(e.getTarget());
20412         if(item){
20413             this.fireEvent("contextmenu", this, this.indexOf(item), item, e);
20414         }
20415     },
20416
20417     /** @ignore */
20418     onDblClick : function(e){
20419         var item = this.findItemFromChild(e.getTarget());
20420         if(item){
20421             this.fireEvent("dblclick", this, this.indexOf(item), item, e);
20422         }
20423     },
20424
20425     onItemClick : function(item, index, e)
20426     {
20427         if(this.fireEvent("beforeclick", this, index, item, e) === false){
20428             return false;
20429         }
20430         if (this.toggleSelect) {
20431             var m = this.isSelected(item) ? 'unselect' : 'select';
20432             //Roo.log(m);
20433             var _t = this;
20434             _t[m](item, true, false);
20435             return true;
20436         }
20437         if(this.multiSelect || this.singleSelect){
20438             if(this.multiSelect && e.shiftKey && this.lastSelection){
20439                 this.select(this.getNodes(this.indexOf(this.lastSelection), index), false);
20440             }else{
20441                 this.select(item, this.multiSelect && e.ctrlKey);
20442                 this.lastSelection = item;
20443             }
20444             
20445             if(!this.tickable){
20446                 e.preventDefault();
20447             }
20448             
20449         }
20450         return true;
20451     },
20452
20453     /**
20454      * Get the number of selected nodes.
20455      * @return {Number}
20456      */
20457     getSelectionCount : function(){
20458         return this.selections.length;
20459     },
20460
20461     /**
20462      * Get the currently selected nodes.
20463      * @return {Array} An array of HTMLElements
20464      */
20465     getSelectedNodes : function(){
20466         return this.selections;
20467     },
20468
20469     /**
20470      * Get the indexes of the selected nodes.
20471      * @return {Array}
20472      */
20473     getSelectedIndexes : function(){
20474         var indexes = [], s = this.selections;
20475         for(var i = 0, len = s.length; i < len; i++){
20476             indexes.push(s[i].nodeIndex);
20477         }
20478         return indexes;
20479     },
20480
20481     /**
20482      * Clear all selections
20483      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange event
20484      */
20485     clearSelections : function(suppressEvent){
20486         if(this.nodes && (this.multiSelect || this.singleSelect) && this.selections.length > 0){
20487             this.cmp.elements = this.selections;
20488             this.cmp.removeClass(this.selectedClass);
20489             this.selections = [];
20490             if(!suppressEvent){
20491                 this.fireEvent("selectionchange", this, this.selections);
20492             }
20493         }
20494     },
20495
20496     /**
20497      * Returns true if the passed node is selected
20498      * @param {HTMLElement/Number} node The node or node index
20499      * @return {Boolean}
20500      */
20501     isSelected : function(node){
20502         var s = this.selections;
20503         if(s.length < 1){
20504             return false;
20505         }
20506         node = this.getNode(node);
20507         return s.indexOf(node) !== -1;
20508     },
20509
20510     /**
20511      * Selects nodes.
20512      * @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
20513      * @param {Boolean} keepExisting (optional) true to keep existing selections
20514      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
20515      */
20516     select : function(nodeInfo, keepExisting, suppressEvent){
20517         if(nodeInfo instanceof Array){
20518             if(!keepExisting){
20519                 this.clearSelections(true);
20520             }
20521             for(var i = 0, len = nodeInfo.length; i < len; i++){
20522                 this.select(nodeInfo[i], true, true);
20523             }
20524             return;
20525         } 
20526         var node = this.getNode(nodeInfo);
20527         if(!node || this.isSelected(node)){
20528             return; // already selected.
20529         }
20530         if(!keepExisting){
20531             this.clearSelections(true);
20532         }
20533         
20534         if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
20535             Roo.fly(node).addClass(this.selectedClass);
20536             this.selections.push(node);
20537             if(!suppressEvent){
20538                 this.fireEvent("selectionchange", this, this.selections);
20539             }
20540         }
20541         
20542         
20543     },
20544       /**
20545      * Unselects nodes.
20546      * @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
20547      * @param {Boolean} keepExisting (optional) true IGNORED (for campatibility with select)
20548      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
20549      */
20550     unselect : function(nodeInfo, keepExisting, suppressEvent)
20551     {
20552         if(nodeInfo instanceof Array){
20553             Roo.each(this.selections, function(s) {
20554                 this.unselect(s, nodeInfo);
20555             }, this);
20556             return;
20557         }
20558         var node = this.getNode(nodeInfo);
20559         if(!node || !this.isSelected(node)){
20560             //Roo.log("not selected");
20561             return; // not selected.
20562         }
20563         // fireevent???
20564         var ns = [];
20565         Roo.each(this.selections, function(s) {
20566             if (s == node ) {
20567                 Roo.fly(node).removeClass(this.selectedClass);
20568
20569                 return;
20570             }
20571             ns.push(s);
20572         },this);
20573         
20574         this.selections= ns;
20575         this.fireEvent("selectionchange", this, this.selections);
20576     },
20577
20578     /**
20579      * Gets a template node.
20580      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
20581      * @return {HTMLElement} The node or null if it wasn't found
20582      */
20583     getNode : function(nodeInfo){
20584         if(typeof nodeInfo == "string"){
20585             return document.getElementById(nodeInfo);
20586         }else if(typeof nodeInfo == "number"){
20587             return this.nodes[nodeInfo];
20588         }
20589         return nodeInfo;
20590     },
20591
20592     /**
20593      * Gets a range template nodes.
20594      * @param {Number} startIndex
20595      * @param {Number} endIndex
20596      * @return {Array} An array of nodes
20597      */
20598     getNodes : function(start, end){
20599         var ns = this.nodes;
20600         start = start || 0;
20601         end = typeof end == "undefined" ? ns.length - 1 : end;
20602         var nodes = [];
20603         if(start <= end){
20604             for(var i = start; i <= end; i++){
20605                 nodes.push(ns[i]);
20606             }
20607         } else{
20608             for(var i = start; i >= end; i--){
20609                 nodes.push(ns[i]);
20610             }
20611         }
20612         return nodes;
20613     },
20614
20615     /**
20616      * Finds the index of the passed node
20617      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
20618      * @return {Number} The index of the node or -1
20619      */
20620     indexOf : function(node){
20621         node = this.getNode(node);
20622         if(typeof node.nodeIndex == "number"){
20623             return node.nodeIndex;
20624         }
20625         var ns = this.nodes;
20626         for(var i = 0, len = ns.length; i < len; i++){
20627             if(ns[i] == node){
20628                 return i;
20629             }
20630         }
20631         return -1;
20632     }
20633 });
20634 /*
20635  * - LGPL
20636  *
20637  * based on jquery fullcalendar
20638  * 
20639  */
20640
20641 Roo.bootstrap = Roo.bootstrap || {};
20642 /**
20643  * @class Roo.bootstrap.Calendar
20644  * @extends Roo.bootstrap.Component
20645  * Bootstrap Calendar class
20646  * @cfg {Boolean} loadMask (true|false) default false
20647  * @cfg {Object} header generate the user specific header of the calendar, default false
20648
20649  * @constructor
20650  * Create a new Container
20651  * @param {Object} config The config object
20652  */
20653
20654
20655
20656 Roo.bootstrap.Calendar = function(config){
20657     Roo.bootstrap.Calendar.superclass.constructor.call(this, config);
20658      this.addEvents({
20659         /**
20660              * @event select
20661              * Fires when a date is selected
20662              * @param {DatePicker} this
20663              * @param {Date} date The selected date
20664              */
20665         'select': true,
20666         /**
20667              * @event monthchange
20668              * Fires when the displayed month changes 
20669              * @param {DatePicker} this
20670              * @param {Date} date The selected month
20671              */
20672         'monthchange': true,
20673         /**
20674              * @event evententer
20675              * Fires when mouse over an event
20676              * @param {Calendar} this
20677              * @param {event} Event
20678              */
20679         'evententer': true,
20680         /**
20681              * @event eventleave
20682              * Fires when the mouse leaves an
20683              * @param {Calendar} this
20684              * @param {event}
20685              */
20686         'eventleave': true,
20687         /**
20688              * @event eventclick
20689              * Fires when the mouse click an
20690              * @param {Calendar} this
20691              * @param {event}
20692              */
20693         'eventclick': true
20694         
20695     });
20696
20697 };
20698
20699 Roo.extend(Roo.bootstrap.Calendar, Roo.bootstrap.Component,  {
20700     
20701           /**
20702      * @cfg {Roo.data.Store} store
20703      * The data source for the calendar
20704      */
20705         store : false,
20706      /**
20707      * @cfg {Number} startDay
20708      * Day index at which the week should begin, 0-based (defaults to 0, which is Sunday)
20709      */
20710     startDay : 0,
20711     
20712     loadMask : false,
20713     
20714     header : false,
20715       
20716     getAutoCreate : function(){
20717         
20718         
20719         var fc_button = function(name, corner, style, content ) {
20720             return Roo.apply({},{
20721                 tag : 'span',
20722                 cls : 'fc-button fc-button-'+name+' fc-state-default ' + 
20723                          (corner.length ?
20724                             'fc-corner-' + corner.split(' ').join(' fc-corner-') :
20725                             ''
20726                         ),
20727                 html : '<SPAN class="fc-text-'+style+ '">'+content +'</SPAN>',
20728                 unselectable: 'on'
20729             });
20730         };
20731         
20732         var header = {};
20733         
20734         if(!this.header){
20735             header = {
20736                 tag : 'table',
20737                 cls : 'fc-header',
20738                 style : 'width:100%',
20739                 cn : [
20740                     {
20741                         tag: 'tr',
20742                         cn : [
20743                             {
20744                                 tag : 'td',
20745                                 cls : 'fc-header-left',
20746                                 cn : [
20747                                     fc_button('prev', 'left', 'arrow', '&#8249;' ),
20748                                     fc_button('next', 'right', 'arrow', '&#8250;' ),
20749                                     { tag: 'span', cls: 'fc-header-space' },
20750                                     fc_button('today', 'left right', '', 'today' )  // neds state disabled..
20751
20752
20753                                 ]
20754                             },
20755
20756                             {
20757                                 tag : 'td',
20758                                 cls : 'fc-header-center',
20759                                 cn : [
20760                                     {
20761                                         tag: 'span',
20762                                         cls: 'fc-header-title',
20763                                         cn : {
20764                                             tag: 'H2',
20765                                             html : 'month / year'
20766                                         }
20767                                     }
20768
20769                                 ]
20770                             },
20771                             {
20772                                 tag : 'td',
20773                                 cls : 'fc-header-right',
20774                                 cn : [
20775                               /*      fc_button('month', 'left', '', 'month' ),
20776                                     fc_button('week', '', '', 'week' ),
20777                                     fc_button('day', 'right', '', 'day' )
20778                                 */    
20779
20780                                 ]
20781                             }
20782
20783                         ]
20784                     }
20785                 ]
20786             };
20787         }
20788         
20789         header = this.header;
20790         
20791        
20792         var cal_heads = function() {
20793             var ret = [];
20794             // fixme - handle this.
20795             
20796             for (var i =0; i < Date.dayNames.length; i++) {
20797                 var d = Date.dayNames[i];
20798                 ret.push({
20799                     tag: 'th',
20800                     cls : 'fc-day-header fc-' + d.substring(0,3).toLowerCase() + ' fc-widget-header',
20801                     html : d.substring(0,3)
20802                 });
20803                 
20804             }
20805             ret[0].cls += ' fc-first';
20806             ret[6].cls += ' fc-last';
20807             return ret;
20808         };
20809         var cal_cell = function(n) {
20810             return  {
20811                 tag: 'td',
20812                 cls : 'fc-day fc-'+n + ' fc-widget-content', ///fc-other-month fc-past
20813                 cn : [
20814                     {
20815                         cn : [
20816                             {
20817                                 cls: 'fc-day-number',
20818                                 html: 'D'
20819                             },
20820                             {
20821                                 cls: 'fc-day-content',
20822                              
20823                                 cn : [
20824                                      {
20825                                         style: 'position: relative;' // height: 17px;
20826                                     }
20827                                 ]
20828                             }
20829                             
20830                             
20831                         ]
20832                     }
20833                 ]
20834                 
20835             }
20836         };
20837         var cal_rows = function() {
20838             
20839             var ret = [];
20840             for (var r = 0; r < 6; r++) {
20841                 var row= {
20842                     tag : 'tr',
20843                     cls : 'fc-week',
20844                     cn : []
20845                 };
20846                 
20847                 for (var i =0; i < Date.dayNames.length; i++) {
20848                     var d = Date.dayNames[i];
20849                     row.cn.push(cal_cell(d.substring(0,3).toLowerCase()));
20850
20851                 }
20852                 row.cn[0].cls+=' fc-first';
20853                 row.cn[0].cn[0].style = 'min-height:90px';
20854                 row.cn[6].cls+=' fc-last';
20855                 ret.push(row);
20856                 
20857             }
20858             ret[0].cls += ' fc-first';
20859             ret[4].cls += ' fc-prev-last';
20860             ret[5].cls += ' fc-last';
20861             return ret;
20862             
20863         };
20864         
20865         var cal_table = {
20866             tag: 'table',
20867             cls: 'fc-border-separate',
20868             style : 'width:100%',
20869             cellspacing  : 0,
20870             cn : [
20871                 { 
20872                     tag: 'thead',
20873                     cn : [
20874                         { 
20875                             tag: 'tr',
20876                             cls : 'fc-first fc-last',
20877                             cn : cal_heads()
20878                         }
20879                     ]
20880                 },
20881                 { 
20882                     tag: 'tbody',
20883                     cn : cal_rows()
20884                 }
20885                   
20886             ]
20887         };
20888          
20889          var cfg = {
20890             cls : 'fc fc-ltr',
20891             cn : [
20892                 header,
20893                 {
20894                     cls : 'fc-content',
20895                     style : "position: relative;",
20896                     cn : [
20897                         {
20898                             cls : 'fc-view fc-view-month fc-grid',
20899                             style : 'position: relative',
20900                             unselectable : 'on',
20901                             cn : [
20902                                 {
20903                                     cls : 'fc-event-container',
20904                                     style : 'position:absolute;z-index:8;top:0;left:0;'
20905                                 },
20906                                 cal_table
20907                             ]
20908                         }
20909                     ]
20910     
20911                 }
20912            ] 
20913             
20914         };
20915         
20916          
20917         
20918         return cfg;
20919     },
20920     
20921     
20922     initEvents : function()
20923     {
20924         if(!this.store){
20925             throw "can not find store for calendar";
20926         }
20927         
20928         var mark = {
20929             tag: "div",
20930             cls:"x-dlg-mask",
20931             style: "text-align:center",
20932             cn: [
20933                 {
20934                     tag: "div",
20935                     style: "background-color:white;width:50%;margin:250 auto",
20936                     cn: [
20937                         {
20938                             tag: "img",
20939                             src: Roo.rootURL + '/images/ux/lightbox/loading.gif' 
20940                         },
20941                         {
20942                             tag: "span",
20943                             html: "Loading"
20944                         }
20945                         
20946                     ]
20947                 }
20948             ]
20949         };
20950         this.maskEl = Roo.DomHelper.append(this.el.select('.fc-content', true).first(), mark, true);
20951         
20952         var size = this.el.select('.fc-content', true).first().getSize();
20953         this.maskEl.setSize(size.width, size.height);
20954         this.maskEl.enableDisplayMode("block");
20955         if(!this.loadMask){
20956             this.maskEl.hide();
20957         }
20958         
20959         this.store = Roo.factory(this.store, Roo.data);
20960         this.store.on('load', this.onLoad, this);
20961         this.store.on('beforeload', this.onBeforeLoad, this);
20962         
20963         this.resize();
20964         
20965         this.cells = this.el.select('.fc-day',true);
20966         //Roo.log(this.cells);
20967         this.textNodes = this.el.query('.fc-day-number');
20968         this.cells.addClassOnOver('fc-state-hover');
20969         
20970         this.el.select('.fc-button-prev',true).on('click', this.showPrevMonth, this);
20971         this.el.select('.fc-button-next',true).on('click', this.showNextMonth, this);
20972         this.el.select('.fc-button-today',true).on('click', this.showToday, this);
20973         this.el.select('.fc-button',true).addClassOnOver('fc-state-hover');
20974         
20975         this.on('monthchange', this.onMonthChange, this);
20976         
20977         this.update(new Date().clearTime());
20978     },
20979     
20980     resize : function() {
20981         var sz  = this.el.getSize();
20982         
20983         this.el.select('.fc-day-header',true).setWidth(sz.width / 7);
20984         this.el.select('.fc-day-content div',true).setHeight(34);
20985     },
20986     
20987     
20988     // private
20989     showPrevMonth : function(e){
20990         this.update(this.activeDate.add("mo", -1));
20991     },
20992     showToday : function(e){
20993         this.update(new Date().clearTime());
20994     },
20995     // private
20996     showNextMonth : function(e){
20997         this.update(this.activeDate.add("mo", 1));
20998     },
20999
21000     // private
21001     showPrevYear : function(){
21002         this.update(this.activeDate.add("y", -1));
21003     },
21004
21005     // private
21006     showNextYear : function(){
21007         this.update(this.activeDate.add("y", 1));
21008     },
21009
21010     
21011    // private
21012     update : function(date)
21013     {
21014         var vd = this.activeDate;
21015         this.activeDate = date;
21016 //        if(vd && this.el){
21017 //            var t = date.getTime();
21018 //            if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
21019 //                Roo.log('using add remove');
21020 //                
21021 //                this.fireEvent('monthchange', this, date);
21022 //                
21023 //                this.cells.removeClass("fc-state-highlight");
21024 //                this.cells.each(function(c){
21025 //                   if(c.dateValue == t){
21026 //                       c.addClass("fc-state-highlight");
21027 //                       setTimeout(function(){
21028 //                            try{c.dom.firstChild.focus();}catch(e){}
21029 //                       }, 50);
21030 //                       return false;
21031 //                   }
21032 //                   return true;
21033 //                });
21034 //                return;
21035 //            }
21036 //        }
21037         
21038         var days = date.getDaysInMonth();
21039         
21040         var firstOfMonth = date.getFirstDateOfMonth();
21041         var startingPos = firstOfMonth.getDay()-this.startDay;
21042         
21043         if(startingPos < this.startDay){
21044             startingPos += 7;
21045         }
21046         
21047         var pm = date.add(Date.MONTH, -1);
21048         var prevStart = pm.getDaysInMonth()-startingPos;
21049 //        
21050         this.cells = this.el.select('.fc-day',true);
21051         this.textNodes = this.el.query('.fc-day-number');
21052         this.cells.addClassOnOver('fc-state-hover');
21053         
21054         var cells = this.cells.elements;
21055         var textEls = this.textNodes;
21056         
21057         Roo.each(cells, function(cell){
21058             cell.removeClass([ 'fc-past', 'fc-other-month', 'fc-future', 'fc-state-highlight', 'fc-state-disabled']);
21059         });
21060         
21061         days += startingPos;
21062
21063         // convert everything to numbers so it's fast
21064         var day = 86400000;
21065         var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
21066         //Roo.log(d);
21067         //Roo.log(pm);
21068         //Roo.log(prevStart);
21069         
21070         var today = new Date().clearTime().getTime();
21071         var sel = date.clearTime().getTime();
21072         var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
21073         var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
21074         var ddMatch = this.disabledDatesRE;
21075         var ddText = this.disabledDatesText;
21076         var ddays = this.disabledDays ? this.disabledDays.join("") : false;
21077         var ddaysText = this.disabledDaysText;
21078         var format = this.format;
21079         
21080         var setCellClass = function(cal, cell){
21081             cell.row = 0;
21082             cell.events = [];
21083             cell.more = [];
21084             //Roo.log('set Cell Class');
21085             cell.title = "";
21086             var t = d.getTime();
21087             
21088             //Roo.log(d);
21089             
21090             cell.dateValue = t;
21091             if(t == today){
21092                 cell.className += " fc-today";
21093                 cell.className += " fc-state-highlight";
21094                 cell.title = cal.todayText;
21095             }
21096             if(t == sel){
21097                 // disable highlight in other month..
21098                 //cell.className += " fc-state-highlight";
21099                 
21100             }
21101             // disabling
21102             if(t < min) {
21103                 cell.className = " fc-state-disabled";
21104                 cell.title = cal.minText;
21105                 return;
21106             }
21107             if(t > max) {
21108                 cell.className = " fc-state-disabled";
21109                 cell.title = cal.maxText;
21110                 return;
21111             }
21112             if(ddays){
21113                 if(ddays.indexOf(d.getDay()) != -1){
21114                     cell.title = ddaysText;
21115                     cell.className = " fc-state-disabled";
21116                 }
21117             }
21118             if(ddMatch && format){
21119                 var fvalue = d.dateFormat(format);
21120                 if(ddMatch.test(fvalue)){
21121                     cell.title = ddText.replace("%0", fvalue);
21122                     cell.className = " fc-state-disabled";
21123                 }
21124             }
21125             
21126             if (!cell.initialClassName) {
21127                 cell.initialClassName = cell.dom.className;
21128             }
21129             
21130             cell.dom.className = cell.initialClassName  + ' ' +  cell.className;
21131         };
21132
21133         var i = 0;
21134         
21135         for(; i < startingPos; i++) {
21136             textEls[i].innerHTML = (++prevStart);
21137             d.setDate(d.getDate()+1);
21138             
21139             cells[i].className = "fc-past fc-other-month";
21140             setCellClass(this, cells[i]);
21141         }
21142         
21143         var intDay = 0;
21144         
21145         for(; i < days; i++){
21146             intDay = i - startingPos + 1;
21147             textEls[i].innerHTML = (intDay);
21148             d.setDate(d.getDate()+1);
21149             
21150             cells[i].className = ''; // "x-date-active";
21151             setCellClass(this, cells[i]);
21152         }
21153         var extraDays = 0;
21154         
21155         for(; i < 42; i++) {
21156             textEls[i].innerHTML = (++extraDays);
21157             d.setDate(d.getDate()+1);
21158             
21159             cells[i].className = "fc-future fc-other-month";
21160             setCellClass(this, cells[i]);
21161         }
21162         
21163         this.el.select('.fc-header-title h2',true).update(Date.monthNames[date.getMonth()] + " " + date.getFullYear());
21164         
21165         var totalRows = Math.ceil((date.getDaysInMonth() + date.getFirstDateOfMonth().getDay()) / 7);
21166         
21167         this.el.select('tr.fc-week.fc-prev-last',true).removeClass('fc-last');
21168         this.el.select('tr.fc-week.fc-next-last',true).addClass('fc-last').show();
21169         
21170         if(totalRows != 6){
21171             this.el.select('tr.fc-week.fc-last',true).removeClass('fc-last').addClass('fc-next-last').hide();
21172             this.el.select('tr.fc-week.fc-prev-last',true).addClass('fc-last');
21173         }
21174         
21175         this.fireEvent('monthchange', this, date);
21176         
21177         
21178         /*
21179         if(!this.internalRender){
21180             var main = this.el.dom.firstChild;
21181             var w = main.offsetWidth;
21182             this.el.setWidth(w + this.el.getBorderWidth("lr"));
21183             Roo.fly(main).setWidth(w);
21184             this.internalRender = true;
21185             // opera does not respect the auto grow header center column
21186             // then, after it gets a width opera refuses to recalculate
21187             // without a second pass
21188             if(Roo.isOpera && !this.secondPass){
21189                 main.rows[0].cells[1].style.width = (w - (main.rows[0].cells[0].offsetWidth+main.rows[0].cells[2].offsetWidth)) + "px";
21190                 this.secondPass = true;
21191                 this.update.defer(10, this, [date]);
21192             }
21193         }
21194         */
21195         
21196     },
21197     
21198     findCell : function(dt) {
21199         dt = dt.clearTime().getTime();
21200         var ret = false;
21201         this.cells.each(function(c){
21202             //Roo.log("check " +c.dateValue + '?=' + dt);
21203             if(c.dateValue == dt){
21204                 ret = c;
21205                 return false;
21206             }
21207             return true;
21208         });
21209         
21210         return ret;
21211     },
21212     
21213     findCells : function(ev) {
21214         var s = ev.start.clone().clearTime().getTime();
21215        // Roo.log(s);
21216         var e= ev.end.clone().clearTime().getTime();
21217        // Roo.log(e);
21218         var ret = [];
21219         this.cells.each(function(c){
21220              ////Roo.log("check " +c.dateValue + '<' + e + ' > ' + s);
21221             
21222             if(c.dateValue > e){
21223                 return ;
21224             }
21225             if(c.dateValue < s){
21226                 return ;
21227             }
21228             ret.push(c);
21229         });
21230         
21231         return ret;    
21232     },
21233     
21234 //    findBestRow: function(cells)
21235 //    {
21236 //        var ret = 0;
21237 //        
21238 //        for (var i =0 ; i < cells.length;i++) {
21239 //            ret  = Math.max(cells[i].rows || 0,ret);
21240 //        }
21241 //        return ret;
21242 //        
21243 //    },
21244     
21245     
21246     addItem : function(ev)
21247     {
21248         // look for vertical location slot in
21249         var cells = this.findCells(ev);
21250         
21251 //        ev.row = this.findBestRow(cells);
21252         
21253         // work out the location.
21254         
21255         var crow = false;
21256         var rows = [];
21257         for(var i =0; i < cells.length; i++) {
21258             
21259             cells[i].row = cells[0].row;
21260             
21261             if(i == 0){
21262                 cells[i].row = cells[i].row + 1;
21263             }
21264             
21265             if (!crow) {
21266                 crow = {
21267                     start : cells[i],
21268                     end :  cells[i]
21269                 };
21270                 continue;
21271             }
21272             if (crow.start.getY() == cells[i].getY()) {
21273                 // on same row.
21274                 crow.end = cells[i];
21275                 continue;
21276             }
21277             // different row.
21278             rows.push(crow);
21279             crow = {
21280                 start: cells[i],
21281                 end : cells[i]
21282             };
21283             
21284         }
21285         
21286         rows.push(crow);
21287         ev.els = [];
21288         ev.rows = rows;
21289         ev.cells = cells;
21290         
21291         cells[0].events.push(ev);
21292         
21293         this.calevents.push(ev);
21294     },
21295     
21296     clearEvents: function() {
21297         
21298         if(!this.calevents){
21299             return;
21300         }
21301         
21302         Roo.each(this.cells.elements, function(c){
21303             c.row = 0;
21304             c.events = [];
21305             c.more = [];
21306         });
21307         
21308         Roo.each(this.calevents, function(e) {
21309             Roo.each(e.els, function(el) {
21310                 el.un('mouseenter' ,this.onEventEnter, this);
21311                 el.un('mouseleave' ,this.onEventLeave, this);
21312                 el.remove();
21313             },this);
21314         },this);
21315         
21316         Roo.each(Roo.select('.fc-more-event', true).elements, function(e){
21317             e.remove();
21318         });
21319         
21320     },
21321     
21322     renderEvents: function()
21323     {   
21324         var _this = this;
21325         
21326         this.cells.each(function(c) {
21327             
21328             if(c.row < 5){
21329                 return;
21330             }
21331             
21332             var ev = c.events;
21333             
21334             var r = 4;
21335             if(c.row != c.events.length){
21336                 r = 4 - (4 - (c.row - c.events.length));
21337             }
21338             
21339             c.events = ev.slice(0, r);
21340             c.more = ev.slice(r);
21341             
21342             if(c.more.length && c.more.length == 1){
21343                 c.events.push(c.more.pop());
21344             }
21345             
21346             c.row = (c.row - ev.length) + c.events.length + ((c.more.length) ? 1 : 0);
21347             
21348         });
21349             
21350         this.cells.each(function(c) {
21351             
21352             c.select('.fc-day-content div',true).first().setHeight(Math.max(34, c.row * 20));
21353             
21354             
21355             for (var e = 0; e < c.events.length; e++){
21356                 var ev = c.events[e];
21357                 var rows = ev.rows;
21358                 
21359                 for(var i = 0; i < rows.length; i++) {
21360                 
21361                     // how many rows should it span..
21362
21363                     var  cfg = {
21364                         cls : 'roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable',
21365                         style : 'position: absolute', // left: 387px; width: 121px; top: 359px;
21366
21367                         unselectable : "on",
21368                         cn : [
21369                             {
21370                                 cls: 'fc-event-inner',
21371                                 cn : [
21372     //                                {
21373     //                                  tag:'span',
21374     //                                  cls: 'fc-event-time',
21375     //                                  html : cells.length > 1 ? '' : ev.time
21376     //                                },
21377                                     {
21378                                       tag:'span',
21379                                       cls: 'fc-event-title',
21380                                       html : String.format('{0}', ev.title)
21381                                     }
21382
21383
21384                                 ]
21385                             },
21386                             {
21387                                 cls: 'ui-resizable-handle ui-resizable-e',
21388                                 html : '&nbsp;&nbsp;&nbsp'
21389                             }
21390
21391                         ]
21392                     };
21393
21394                     if (i == 0) {
21395                         cfg.cls += ' fc-event-start';
21396                     }
21397                     if ((i+1) == rows.length) {
21398                         cfg.cls += ' fc-event-end';
21399                     }
21400
21401                     var ctr = _this.el.select('.fc-event-container',true).first();
21402                     var cg = ctr.createChild(cfg);
21403
21404                     var sbox = rows[i].start.select('.fc-day-content',true).first().getBox();
21405                     var ebox = rows[i].end.select('.fc-day-content',true).first().getBox();
21406
21407                     var r = (c.more.length) ? 1 : 0;
21408                     cg.setXY([sbox.x +2, sbox.y + ((c.row - c.events.length - r + e) * 20)]);    
21409                     cg.setWidth(ebox.right - sbox.x -2);
21410
21411                     cg.on('mouseenter' ,_this.onEventEnter, _this, ev);
21412                     cg.on('mouseleave' ,_this.onEventLeave, _this, ev);
21413                     cg.on('click', _this.onEventClick, _this, ev);
21414
21415                     ev.els.push(cg);
21416                     
21417                 }
21418                 
21419             }
21420             
21421             
21422             if(c.more.length){
21423                 var  cfg = {
21424                     cls : 'fc-more-event roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable fc-event-start fc-event-end',
21425                     style : 'position: absolute',
21426                     unselectable : "on",
21427                     cn : [
21428                         {
21429                             cls: 'fc-event-inner',
21430                             cn : [
21431                                 {
21432                                   tag:'span',
21433                                   cls: 'fc-event-title',
21434                                   html : 'More'
21435                                 }
21436
21437
21438                             ]
21439                         },
21440                         {
21441                             cls: 'ui-resizable-handle ui-resizable-e',
21442                             html : '&nbsp;&nbsp;&nbsp'
21443                         }
21444
21445                     ]
21446                 };
21447
21448                 var ctr = _this.el.select('.fc-event-container',true).first();
21449                 var cg = ctr.createChild(cfg);
21450
21451                 var sbox = c.select('.fc-day-content',true).first().getBox();
21452                 var ebox = c.select('.fc-day-content',true).first().getBox();
21453                 //Roo.log(cg);
21454                 cg.setXY([sbox.x +2, sbox.y +((c.row - 1) * 20)]);    
21455                 cg.setWidth(ebox.right - sbox.x -2);
21456
21457                 cg.on('click', _this.onMoreEventClick, _this, c.more);
21458                 
21459             }
21460             
21461         });
21462         
21463         
21464         
21465     },
21466     
21467     onEventEnter: function (e, el,event,d) {
21468         this.fireEvent('evententer', this, el, event);
21469     },
21470     
21471     onEventLeave: function (e, el,event,d) {
21472         this.fireEvent('eventleave', this, el, event);
21473     },
21474     
21475     onEventClick: function (e, el,event,d) {
21476         this.fireEvent('eventclick', this, el, event);
21477     },
21478     
21479     onMonthChange: function () {
21480         this.store.load();
21481     },
21482     
21483     onMoreEventClick: function(e, el, more)
21484     {
21485         var _this = this;
21486         
21487         this.calpopover.placement = 'right';
21488         this.calpopover.setTitle('More');
21489         
21490         this.calpopover.setContent('');
21491         
21492         var ctr = this.calpopover.el.select('.popover-content', true).first();
21493         
21494         Roo.each(more, function(m){
21495             var cfg = {
21496                 cls : 'fc-event-hori fc-event-draggable',
21497                 html : m.title
21498             };
21499             var cg = ctr.createChild(cfg);
21500             
21501             cg.on('click', _this.onEventClick, _this, m);
21502         });
21503         
21504         this.calpopover.show(el);
21505         
21506         
21507     },
21508     
21509     onLoad: function () 
21510     {   
21511         this.calevents = [];
21512         var cal = this;
21513         
21514         if(this.store.getCount() > 0){
21515             this.store.data.each(function(d){
21516                cal.addItem({
21517                     id : d.data.id,
21518                     start: (typeof(d.data.start_dt) === 'string') ? new Date.parseDate(d.data.start_dt, 'Y-m-d H:i:s') : d.data.start_dt,
21519                     end : (typeof(d.data.end_dt) === 'string') ? new Date.parseDate(d.data.end_dt, 'Y-m-d H:i:s') : d.data.end_dt,
21520                     time : d.data.start_time,
21521                     title : d.data.title,
21522                     description : d.data.description,
21523                     venue : d.data.venue
21524                 });
21525             });
21526         }
21527         
21528         this.renderEvents();
21529         
21530         if(this.calevents.length && this.loadMask){
21531             this.maskEl.hide();
21532         }
21533     },
21534     
21535     onBeforeLoad: function()
21536     {
21537         this.clearEvents();
21538         if(this.loadMask){
21539             this.maskEl.show();
21540         }
21541     }
21542 });
21543
21544  
21545  /*
21546  * - LGPL
21547  *
21548  * element
21549  * 
21550  */
21551
21552 /**
21553  * @class Roo.bootstrap.Popover
21554  * @extends Roo.bootstrap.Component
21555  * @parent none builder
21556  * @children Roo.bootstrap.Component
21557  * Bootstrap Popover class
21558  * @cfg {String} html contents of the popover   (or false to use children..)
21559  * @cfg {String} title of popover (or false to hide)
21560  * @cfg {String|function} (right|top|bottom|left|auto) placement how it is placed
21561  * @cfg {String} trigger click || hover (or false to trigger manually)
21562  * @cfg {Boolean} modal - popovers that are modal will mask the screen, and must be closed with another event.
21563  * @cfg {String|Boolean|Roo.Element} add click hander to trigger show over what element
21564  *      - if false and it has a 'parent' then it will be automatically added to that element
21565  *      - if string - Roo.get  will be called 
21566  * @cfg {Number} delay - delay before showing
21567  
21568  * @constructor
21569  * Create a new Popover
21570  * @param {Object} config The config object
21571  */
21572
21573 Roo.bootstrap.Popover = function(config){
21574     Roo.bootstrap.Popover.superclass.constructor.call(this, config);
21575     
21576     this.addEvents({
21577         // raw events
21578          /**
21579          * @event show
21580          * After the popover show
21581          * 
21582          * @param {Roo.bootstrap.Popover} this
21583          */
21584         "show" : true,
21585         /**
21586          * @event hide
21587          * After the popover hide
21588          * 
21589          * @param {Roo.bootstrap.Popover} this
21590          */
21591         "hide" : true
21592     });
21593 };
21594
21595 Roo.extend(Roo.bootstrap.Popover, Roo.bootstrap.Component,  {
21596     
21597     title: false,
21598     html: false,
21599     
21600     placement : 'right',
21601     trigger : 'hover', // hover
21602     modal : false,
21603     delay : 0,
21604     
21605     over: false,
21606     
21607     can_build_overlaid : false,
21608     
21609     maskEl : false, // the mask element
21610     headerEl : false,
21611     contentEl : false,
21612     alignEl : false, // when show is called with an element - this get's stored.
21613     
21614     getChildContainer : function()
21615     {
21616         return this.contentEl;
21617         
21618     },
21619     getPopoverHeader : function()
21620     {
21621         this.title = true; // flag not to hide it..
21622         this.headerEl.addClass('p-0');
21623         return this.headerEl
21624     },
21625     
21626     
21627     getAutoCreate : function(){
21628          
21629         var cfg = {
21630            cls : 'popover roo-dynamic shadow roo-popover' + (this.modal ? '-modal' : ''),
21631            style: 'display:block',
21632            cn : [
21633                 {
21634                     cls : 'arrow'
21635                 },
21636                 {
21637                     cls : 'popover-inner ',
21638                     cn : [
21639                         {
21640                             tag: 'h3',
21641                             cls: 'popover-title popover-header',
21642                             html : this.title === false ? '' : this.title
21643                         },
21644                         {
21645                             cls : 'popover-content popover-body '  + (this.cls || ''),
21646                             html : this.html || ''
21647                         }
21648                     ]
21649                     
21650                 }
21651            ]
21652         };
21653         
21654         return cfg;
21655     },
21656     /**
21657      * @param {string} the title
21658      */
21659     setTitle: function(str)
21660     {
21661         this.title = str;
21662         if (this.el) {
21663             this.headerEl.dom.innerHTML = str;
21664         }
21665         
21666     },
21667     /**
21668      * @param {string} the body content
21669      */
21670     setContent: function(str)
21671     {
21672         this.html = str;
21673         if (this.contentEl) {
21674             this.contentEl.dom.innerHTML = str;
21675         }
21676         
21677     },
21678     // as it get's added to the bottom of the page.
21679     onRender : function(ct, position)
21680     {
21681         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
21682         
21683         
21684         
21685         if(!this.el){
21686             var cfg = Roo.apply({},  this.getAutoCreate());
21687             cfg.id = Roo.id();
21688             
21689             if (this.cls) {
21690                 cfg.cls += ' ' + this.cls;
21691             }
21692             if (this.style) {
21693                 cfg.style = this.style;
21694             }
21695             //Roo.log("adding to ");
21696             this.el = Roo.get(document.body).createChild(cfg, position);
21697 //            Roo.log(this.el);
21698         }
21699         
21700         this.contentEl = this.el.select('.popover-content',true).first();
21701         this.headerEl =  this.el.select('.popover-title',true).first();
21702         
21703         var nitems = [];
21704         if(typeof(this.items) != 'undefined'){
21705             var items = this.items;
21706             delete this.items;
21707
21708             for(var i =0;i < items.length;i++) {
21709                 nitems.push(this.addxtype(Roo.apply({}, items[i])));
21710             }
21711         }
21712
21713         this.items = nitems;
21714         
21715         this.maskEl = Roo.DomHelper.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
21716         Roo.EventManager.onWindowResize(this.resizeMask, this, true);
21717         
21718         
21719         
21720         this.initEvents();
21721     },
21722     
21723     resizeMask : function()
21724     {
21725         this.maskEl.setSize(
21726             Roo.lib.Dom.getViewWidth(true),
21727             Roo.lib.Dom.getViewHeight(true)
21728         );
21729     },
21730     
21731     initEvents : function()
21732     {
21733         
21734         if (!this.modal) { 
21735             Roo.bootstrap.Popover.register(this);
21736         }
21737          
21738         this.arrowEl = this.el.select('.arrow',true).first();
21739         this.headerEl.setVisibilityMode(Roo.Element.DISPLAY); // probably not needed as it's default in BS4
21740         this.el.enableDisplayMode('block');
21741         this.el.hide();
21742  
21743         
21744         if (this.over === false && !this.parent()) {
21745             return; 
21746         }
21747         if (this.triggers === false) {
21748             return;
21749         }
21750          
21751         // support parent
21752         var on_el = (this.over == 'parent' || this.over === false) ? this.parent().el : Roo.get(this.over);
21753         var triggers = this.trigger ? this.trigger.split(' ') : [];
21754         Roo.each(triggers, function(trigger) {
21755         
21756             if (trigger == 'click') {
21757                 on_el.on('click', this.toggle, this);
21758             } else if (trigger != 'manual') {
21759                 var eventIn  = trigger == 'hover' ? 'mouseenter' : 'focusin';
21760                 var eventOut = trigger == 'hover' ? 'mouseleave' : 'focusout';
21761       
21762                 on_el.on(eventIn  ,this.enter, this);
21763                 on_el.on(eventOut, this.leave, this);
21764             }
21765         }, this);
21766     },
21767     
21768     
21769     // private
21770     timeout : null,
21771     hoverState : null,
21772     
21773     toggle : function () {
21774         this.hoverState == 'in' ? this.leave() : this.enter();
21775     },
21776     
21777     enter : function () {
21778         
21779         clearTimeout(this.timeout);
21780     
21781         this.hoverState = 'in';
21782     
21783         if (!this.delay || !this.delay.show) {
21784             this.show();
21785             return;
21786         }
21787         var _t = this;
21788         this.timeout = setTimeout(function () {
21789             if (_t.hoverState == 'in') {
21790                 _t.show();
21791             }
21792         }, this.delay.show)
21793     },
21794     
21795     leave : function() {
21796         clearTimeout(this.timeout);
21797     
21798         this.hoverState = 'out';
21799     
21800         if (!this.delay || !this.delay.hide) {
21801             this.hide();
21802             return;
21803         }
21804         var _t = this;
21805         this.timeout = setTimeout(function () {
21806             if (_t.hoverState == 'out') {
21807                 _t.hide();
21808             }
21809         }, this.delay.hide)
21810     },
21811     
21812     /**
21813      * update the position of the dialog
21814      * normally this is needed if the popover get's bigger - due to a Table reload etc..
21815      * 
21816      *
21817      */
21818     
21819     doAlign : function()
21820     {
21821         
21822         if (this.alignEl) {
21823             this.updatePosition(this.placement, true);
21824              
21825         } else {
21826             // this is usually just done by the builder = to show the popoup in the middle of the scren.
21827             var es = this.el.getSize();
21828             var x = Roo.lib.Dom.getViewWidth()/2;
21829             var y = Roo.lib.Dom.getViewHeight()/2;
21830             this.el.setXY([ x-(es.width/2),  y-(es.height/2)] );
21831             
21832         }
21833
21834          
21835          
21836         
21837         
21838     },
21839     
21840     /**
21841      * Show the popover
21842      * @param {Roo.Element|string|Boolean} - element to align and point to. (set align to [ pos, offset ])
21843      * @param {string} (left|right|top|bottom) position
21844      */
21845     show : function (on_el, placement)
21846     {
21847         this.placement = typeof(placement) == 'undefined' ?  this.placement   : placement;
21848         on_el = on_el || false; // default to false
21849          
21850         if (!on_el) {
21851             if (this.parent() && (this.over == 'parent' || (this.over === false))) {
21852                 on_el = this.parent().el;
21853             } else if (this.over) {
21854                 on_el = Roo.get(this.over);
21855             }
21856             
21857         }
21858         
21859         this.alignEl = Roo.get( on_el );
21860
21861         if (!this.el) {
21862             this.render(document.body);
21863         }
21864         
21865         
21866          
21867         
21868         if (this.title === false) {
21869             this.headerEl.hide();
21870         }
21871         
21872        
21873         this.el.show();
21874         this.el.dom.style.display = 'block';
21875          
21876         this.doAlign();
21877         
21878         //var arrow = this.el.select('.arrow',true).first();
21879         //arrow.set(align[2], 
21880         
21881         this.el.addClass('in');
21882         
21883          
21884         
21885         this.hoverState = 'in';
21886         
21887         if (this.modal) {
21888             this.maskEl.setSize(Roo.lib.Dom.getViewWidth(true),   Roo.lib.Dom.getViewHeight(true));
21889             this.maskEl.setStyle('z-index', Roo.bootstrap.Popover.zIndex++);
21890             this.maskEl.dom.style.display = 'block';
21891             this.maskEl.addClass('show');
21892         }
21893         this.el.setStyle('z-index', Roo.bootstrap.Popover.zIndex++);
21894  
21895         this.fireEvent('show', this);
21896         
21897     },
21898     /**
21899      * fire this manually after loading a grid in the table for example
21900      * @param {string} (left|right|top|bottom) where to try and put it (use false to use the last one)
21901      * @param {Boolean} try and move it if we cant get right position.
21902      */
21903     updatePosition : function(placement, try_move)
21904     {
21905         // allow for calling with no parameters
21906         placement = placement   ? placement :  this.placement;
21907         try_move = typeof(try_move) == 'undefined' ? true : try_move;
21908         
21909         this.el.removeClass([
21910             'fade','top','bottom', 'left', 'right','in',
21911             'bs-popover-top','bs-popover-bottom', 'bs-popover-left', 'bs-popover-right'
21912         ]);
21913         this.el.addClass(placement + ' bs-popover-' + placement);
21914         
21915         if (!this.alignEl ) {
21916             return false;
21917         }
21918         
21919         switch (placement) {
21920             case 'right':
21921                 var exact = this.el.getAlignToXY(this.alignEl, 'tl-tr', [10,0]);
21922                 var offset = this.el.getAlignToXY(this.alignEl, 'tl-tr?',[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('tr', false);
21927                     xy[0]+=2;xy[1]+=5;
21928                     this.arrowEl.setXY(xy);
21929                     return true;
21930                 }
21931                 // continue through...
21932                 return this.updatePosition('left', false);
21933                 
21934             
21935             case 'left':
21936                 var exact = this.el.getAlignToXY(this.alignEl, 'tr-tl', [-10,0]);
21937                 var offset = this.el.getAlignToXY(this.alignEl, 'tr-tl?',[-10,0]);
21938                 if (!try_move || exact.equals(offset) || exact[0] == offset[0] ) {
21939                     //normal display... or moved up/down.
21940                     this.el.setXY(offset);
21941                     var xy = this.alignEl.getAnchorXY('tl', false);
21942                     xy[0]-=10;xy[1]+=5; // << fix me
21943                     this.arrowEl.setXY(xy);
21944                     return true;
21945                 }
21946                 // call self...
21947                 return this.updatePosition('right', false);
21948             
21949             case 'top':
21950                 var exact = this.el.getAlignToXY(this.alignEl, 'b-t', [0,-10]);
21951                 var offset = this.el.getAlignToXY(this.alignEl, 'b-t?',[0,-10]);
21952                 if (!try_move || exact.equals(offset) || exact[1] == offset[1] ) {
21953                     //normal display... or moved up/down.
21954                     this.el.setXY(offset);
21955                     var xy = this.alignEl.getAnchorXY('t', false);
21956                     xy[1]-=10; // << fix me
21957                     this.arrowEl.setXY(xy);
21958                     return true;
21959                 }
21960                 // fall through
21961                return this.updatePosition('bottom', false);
21962             
21963             case 'bottom':
21964                  var exact = this.el.getAlignToXY(this.alignEl, 't-b', [0,10]);
21965                 var offset = this.el.getAlignToXY(this.alignEl, 't-b?',[0,10]);
21966                 if (!try_move || exact.equals(offset) || exact[1] == offset[1] ) {
21967                     //normal display... or moved up/down.
21968                     this.el.setXY(offset);
21969                     var xy = this.alignEl.getAnchorXY('b', false);
21970                      xy[1]+=2; // << fix me
21971                     this.arrowEl.setXY(xy);
21972                     return true;
21973                 }
21974                 // fall through
21975                 return this.updatePosition('top', false);
21976                 
21977             
21978         }
21979         
21980         
21981         return false;
21982     },
21983     
21984     hide : function()
21985     {
21986         this.el.setXY([0,0]);
21987         this.el.removeClass('in');
21988         this.el.hide();
21989         this.hoverState = null;
21990         this.maskEl.hide(); // always..
21991         this.fireEvent('hide', this);
21992     }
21993     
21994 });
21995
21996
21997 Roo.apply(Roo.bootstrap.Popover, {
21998
21999     alignment : {
22000         'left' : ['r-l', [-10,0], 'left bs-popover-left'],
22001         'right' : ['l-br', [10,0], 'right bs-popover-right'],
22002         'bottom' : ['t-b', [0,10], 'top bs-popover-top'],
22003         'top' : [ 'b-t', [0,-10], 'bottom bs-popover-bottom']
22004     },
22005     
22006     zIndex : 20001,
22007
22008     clickHander : false,
22009     
22010     
22011
22012     onMouseDown : function(e)
22013     {
22014         if (this.popups.length &&  !e.getTarget(".roo-popover")) {
22015             /// what is nothing is showing..
22016             this.hideAll();
22017         }
22018          
22019     },
22020     
22021     
22022     popups : [],
22023     
22024     register : function(popup)
22025     {
22026         if (!Roo.bootstrap.Popover.clickHandler) {
22027             Roo.bootstrap.Popover.clickHandler = Roo.get(document).on("mousedown", Roo.bootstrap.Popover.onMouseDown, Roo.bootstrap.Popover);
22028         }
22029         // hide other popups.
22030         popup.on('show', Roo.bootstrap.Popover.onShow,  popup);
22031         popup.on('hide', Roo.bootstrap.Popover.onHide,  popup);
22032         this.hideAll(); //<< why?
22033         //this.popups.push(popup);
22034     },
22035     hideAll : function()
22036     {
22037         this.popups.forEach(function(p) {
22038             p.hide();
22039         });
22040     },
22041     onShow : function() {
22042         Roo.bootstrap.Popover.popups.push(this);
22043     },
22044     onHide : function() {
22045         Roo.bootstrap.Popover.popups.remove(this);
22046     } 
22047
22048 });
22049 /**
22050  * @class Roo.bootstrap.PopoverNav
22051  * @extends Roo.bootstrap.nav.Simplebar
22052  * @parent Roo.bootstrap.Popover
22053  * @children Roo.bootstrap.nav.Group Roo.bootstrap.Container
22054  * @licence LGPL
22055  * Bootstrap Popover header navigation class
22056  * FIXME? should this go under nav?
22057  *
22058  * 
22059  * @constructor
22060  * Create a new Popover Header Navigation 
22061  * @param {Object} config The config object
22062  */
22063
22064 Roo.bootstrap.PopoverNav = function(config){
22065     Roo.bootstrap.PopoverNav.superclass.constructor.call(this, config);
22066 };
22067
22068 Roo.extend(Roo.bootstrap.PopoverNav, Roo.bootstrap.nav.Simplebar,  {
22069     
22070     
22071     container_method : 'getPopoverHeader' 
22072     
22073      
22074     
22075     
22076    
22077 });
22078
22079  
22080
22081  /*
22082  * - LGPL
22083  *
22084  * Progress
22085  * 
22086  */
22087
22088 /**
22089  * @class Roo.bootstrap.Progress
22090  * @extends Roo.bootstrap.Component
22091  * @children Roo.bootstrap.ProgressBar
22092  * Bootstrap Progress class
22093  * @cfg {Boolean} striped striped of the progress bar
22094  * @cfg {Boolean} active animated of the progress bar
22095  * 
22096  * 
22097  * @constructor
22098  * Create a new Progress
22099  * @param {Object} config The config object
22100  */
22101
22102 Roo.bootstrap.Progress = function(config){
22103     Roo.bootstrap.Progress.superclass.constructor.call(this, config);
22104 };
22105
22106 Roo.extend(Roo.bootstrap.Progress, Roo.bootstrap.Component,  {
22107     
22108     striped : false,
22109     active: false,
22110     
22111     getAutoCreate : function(){
22112         var cfg = {
22113             tag: 'div',
22114             cls: 'progress'
22115         };
22116         
22117         
22118         if(this.striped){
22119             cfg.cls += ' progress-striped';
22120         }
22121       
22122         if(this.active){
22123             cfg.cls += ' active';
22124         }
22125         
22126         
22127         return cfg;
22128     }
22129    
22130 });
22131
22132  
22133
22134  /*
22135  * - LGPL
22136  *
22137  * ProgressBar
22138  * 
22139  */
22140
22141 /**
22142  * @class Roo.bootstrap.ProgressBar
22143  * @extends Roo.bootstrap.Component
22144  * Bootstrap ProgressBar class
22145  * @cfg {Number} aria_valuenow aria-value now
22146  * @cfg {Number} aria_valuemin aria-value min
22147  * @cfg {Number} aria_valuemax aria-value max
22148  * @cfg {String} label label for the progress bar
22149  * @cfg {String} panel (success | info | warning | danger )
22150  * @cfg {String} role role of the progress bar
22151  * @cfg {String} sr_only text
22152  * 
22153  * 
22154  * @constructor
22155  * Create a new ProgressBar
22156  * @param {Object} config The config object
22157  */
22158
22159 Roo.bootstrap.ProgressBar = function(config){
22160     Roo.bootstrap.ProgressBar.superclass.constructor.call(this, config);
22161 };
22162
22163 Roo.extend(Roo.bootstrap.ProgressBar, Roo.bootstrap.Component,  {
22164     
22165     aria_valuenow : 0,
22166     aria_valuemin : 0,
22167     aria_valuemax : 100,
22168     label : false,
22169     panel : false,
22170     role : false,
22171     sr_only: false,
22172     
22173     getAutoCreate : function()
22174     {
22175         
22176         var cfg = {
22177             tag: 'div',
22178             cls: 'progress-bar',
22179             style: 'width:' + Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%'
22180         };
22181         
22182         if(this.sr_only){
22183             cfg.cn = {
22184                 tag: 'span',
22185                 cls: 'sr-only',
22186                 html: this.sr_only
22187             }
22188         }
22189         
22190         if(this.role){
22191             cfg.role = this.role;
22192         }
22193         
22194         if(this.aria_valuenow){
22195             cfg['aria-valuenow'] = this.aria_valuenow;
22196         }
22197         
22198         if(this.aria_valuemin){
22199             cfg['aria-valuemin'] = this.aria_valuemin;
22200         }
22201         
22202         if(this.aria_valuemax){
22203             cfg['aria-valuemax'] = this.aria_valuemax;
22204         }
22205         
22206         if(this.label && !this.sr_only){
22207             cfg.html = this.label;
22208         }
22209         
22210         if(this.panel){
22211             cfg.cls += ' progress-bar-' + this.panel;
22212         }
22213         
22214         return cfg;
22215     },
22216     
22217     update : function(aria_valuenow)
22218     {
22219         this.aria_valuenow = aria_valuenow;
22220         
22221         this.el.setStyle('width', Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%');
22222     }
22223    
22224 });
22225
22226  
22227
22228  /**
22229  * @class Roo.bootstrap.TabGroup
22230  * @extends Roo.bootstrap.Column
22231  * @children Roo.bootstrap.TabPanel
22232  * Bootstrap Column class
22233  * @cfg {String} navId the navigation id (for use with navbars) - will be auto generated if it does not exist..
22234  * @cfg {Boolean} carousel true to make the group behave like a carousel
22235  * @cfg {Boolean} bullets show bullets for the panels
22236  * @cfg {Boolean} autoslide (true|false) auto slide .. default false
22237  * @cfg {Number} timer auto slide timer .. default 0 millisecond
22238  * @cfg {Boolean} showarrow (true|false) show arrow default true
22239  * 
22240  * @constructor
22241  * Create a new TabGroup
22242  * @param {Object} config The config object
22243  */
22244
22245 Roo.bootstrap.TabGroup = function(config){
22246     Roo.bootstrap.TabGroup.superclass.constructor.call(this, config);
22247     if (!this.navId) {
22248         this.navId = Roo.id();
22249     }
22250     this.tabs = [];
22251     Roo.bootstrap.TabGroup.register(this);
22252     
22253 };
22254
22255 Roo.extend(Roo.bootstrap.TabGroup, Roo.bootstrap.Column,  {
22256     
22257     carousel : false,
22258     transition : false,
22259     bullets : 0,
22260     timer : 0,
22261     autoslide : false,
22262     slideFn : false,
22263     slideOnTouch : false,
22264     showarrow : true,
22265     
22266     getAutoCreate : function()
22267     {
22268         var cfg = Roo.apply({}, Roo.bootstrap.TabGroup.superclass.getAutoCreate.call(this));
22269         
22270         cfg.cls += ' tab-content';
22271         
22272         if (this.carousel) {
22273             cfg.cls += ' carousel slide';
22274             
22275             cfg.cn = [{
22276                cls : 'carousel-inner',
22277                cn : []
22278             }];
22279         
22280             if(this.bullets  && !Roo.isTouch){
22281                 
22282                 var bullets = {
22283                     cls : 'carousel-bullets',
22284                     cn : []
22285                 };
22286                
22287                 if(this.bullets_cls){
22288                     bullets.cls = bullets.cls + ' ' + this.bullets_cls;
22289                 }
22290                 
22291                 bullets.cn.push({
22292                     cls : 'clear'
22293                 });
22294                 
22295                 cfg.cn[0].cn.push(bullets);
22296             }
22297             
22298             if(this.showarrow){
22299                 cfg.cn[0].cn.push({
22300                     tag : 'div',
22301                     class : 'carousel-arrow',
22302                     cn : [
22303                         {
22304                             tag : 'div',
22305                             class : 'carousel-prev',
22306                             cn : [
22307                                 {
22308                                     tag : 'i',
22309                                     class : 'fa fa-chevron-left'
22310                                 }
22311                             ]
22312                         },
22313                         {
22314                             tag : 'div',
22315                             class : 'carousel-next',
22316                             cn : [
22317                                 {
22318                                     tag : 'i',
22319                                     class : 'fa fa-chevron-right'
22320                                 }
22321                             ]
22322                         }
22323                     ]
22324                 });
22325             }
22326             
22327         }
22328         
22329         return cfg;
22330     },
22331     
22332     initEvents:  function()
22333     {
22334 //        if(Roo.isTouch && this.slideOnTouch && !this.showarrow){
22335 //            this.el.on("touchstart", this.onTouchStart, this);
22336 //        }
22337         
22338         if(this.autoslide){
22339             var _this = this;
22340             
22341             this.slideFn = window.setInterval(function() {
22342                 _this.showPanelNext();
22343             }, this.timer);
22344         }
22345         
22346         if(this.showarrow){
22347             this.el.select('.carousel-prev', true).first().on('click', this.showPanelPrev, this);
22348             this.el.select('.carousel-next', true).first().on('click', this.showPanelNext, this);
22349         }
22350         
22351         
22352     },
22353     
22354 //    onTouchStart : function(e, el, o)
22355 //    {
22356 //        if(!this.slideOnTouch || !Roo.isTouch || Roo.get(e.getTarget()).hasClass('roo-button-text')){
22357 //            return;
22358 //        }
22359 //        
22360 //        this.showPanelNext();
22361 //    },
22362     
22363     
22364     getChildContainer : function()
22365     {
22366         return this.carousel ? this.el.select('.carousel-inner', true).first() : this.el;
22367     },
22368     
22369     /**
22370     * register a Navigation item
22371     * @param {Roo.bootstrap.nav.Item} the navitem to add
22372     */
22373     register : function(item)
22374     {
22375         this.tabs.push( item);
22376         item.navId = this.navId; // not really needed..
22377         this.addBullet();
22378     
22379     },
22380     
22381     getActivePanel : function()
22382     {
22383         var r = false;
22384         Roo.each(this.tabs, function(t) {
22385             if (t.active) {
22386                 r = t;
22387                 return false;
22388             }
22389             return null;
22390         });
22391         return r;
22392         
22393     },
22394     getPanelByName : function(n)
22395     {
22396         var r = false;
22397         Roo.each(this.tabs, function(t) {
22398             if (t.tabId == n) {
22399                 r = t;
22400                 return false;
22401             }
22402             return null;
22403         });
22404         return r;
22405     },
22406     indexOfPanel : function(p)
22407     {
22408         var r = false;
22409         Roo.each(this.tabs, function(t,i) {
22410             if (t.tabId == p.tabId) {
22411                 r = i;
22412                 return false;
22413             }
22414             return null;
22415         });
22416         return r;
22417     },
22418     /**
22419      * show a specific panel
22420      * @param {Roo.bootstrap.TabPanel|number|string} panel to change to (use the tabId to specify a specific one)
22421      * @return {boolean} false if panel was not shown (invalid entry or beforedeactivate fails.)
22422      */
22423     showPanel : function (pan)
22424     {
22425         if(this.transition || typeof(pan) == 'undefined'){
22426             Roo.log("waiting for the transitionend");
22427             return false;
22428         }
22429         
22430         if (typeof(pan) == 'number') {
22431             pan = this.tabs[pan];
22432         }
22433         
22434         if (typeof(pan) == 'string') {
22435             pan = this.getPanelByName(pan);
22436         }
22437         
22438         var cur = this.getActivePanel();
22439         
22440         if(!pan || !cur){
22441             Roo.log('pan or acitve pan is undefined');
22442             return false;
22443         }
22444         
22445         if (pan.tabId == this.getActivePanel().tabId) {
22446             return true;
22447         }
22448         
22449         if (false === cur.fireEvent('beforedeactivate')) {
22450             return false;
22451         }
22452         
22453         if(this.bullets > 0 && !Roo.isTouch){
22454             this.setActiveBullet(this.indexOfPanel(pan));
22455         }
22456         
22457         if (this.carousel && typeof(Roo.get(document.body).dom.style.transition) != 'undefined') {
22458             
22459             //class="carousel-item carousel-item-next carousel-item-left"
22460             
22461             this.transition = true;
22462             var dir = this.indexOfPanel(pan) > this.indexOfPanel(cur)  ? 'next' : 'prev';
22463             var lr = dir == 'next' ? 'left' : 'right';
22464             pan.el.addClass(dir); // or prev
22465             pan.el.addClass('carousel-item-' + dir); // or prev
22466             pan.el.dom.offsetWidth; // find the offset with - causing a reflow?
22467             cur.el.addClass(lr); // or right
22468             pan.el.addClass(lr);
22469             cur.el.addClass('carousel-item-' +lr); // or right
22470             pan.el.addClass('carousel-item-' +lr);
22471             
22472             
22473             var _this = this;
22474             cur.el.on('transitionend', function() {
22475                 Roo.log("trans end?");
22476                 
22477                 pan.el.removeClass([lr,dir, 'carousel-item-' + lr, 'carousel-item-' + dir]);
22478                 pan.setActive(true);
22479                 
22480                 cur.el.removeClass([lr, 'carousel-item-' + lr]);
22481                 cur.setActive(false);
22482                 
22483                 _this.transition = false;
22484                 
22485             }, this, { single:  true } );
22486             
22487             return true;
22488         }
22489         
22490         cur.setActive(false);
22491         pan.setActive(true);
22492         
22493         return true;
22494         
22495     },
22496     showPanelNext : function()
22497     {
22498         var i = this.indexOfPanel(this.getActivePanel());
22499         
22500         if (i >= this.tabs.length - 1 && !this.autoslide) {
22501             return;
22502         }
22503         
22504         if (i >= this.tabs.length - 1 && this.autoslide) {
22505             i = -1;
22506         }
22507         
22508         this.showPanel(this.tabs[i+1]);
22509     },
22510     
22511     showPanelPrev : function()
22512     {
22513         var i = this.indexOfPanel(this.getActivePanel());
22514         
22515         if (i  < 1 && !this.autoslide) {
22516             return;
22517         }
22518         
22519         if (i < 1 && this.autoslide) {
22520             i = this.tabs.length;
22521         }
22522         
22523         this.showPanel(this.tabs[i-1]);
22524     },
22525     
22526     
22527     addBullet: function()
22528     {
22529         if(!this.bullets || Roo.isTouch){
22530             return;
22531         }
22532         var ctr = this.el.select('.carousel-bullets',true).first();
22533         var i = this.el.select('.carousel-bullets .bullet',true).getCount() ;
22534         var bullet = ctr.createChild({
22535             cls : 'bullet bullet-' + i
22536         },ctr.dom.lastChild);
22537         
22538         
22539         var _this = this;
22540         
22541         bullet.on('click', (function(e, el, o, ii, t){
22542
22543             e.preventDefault();
22544
22545             this.showPanel(ii);
22546
22547             if(this.autoslide && this.slideFn){
22548                 clearInterval(this.slideFn);
22549                 this.slideFn = window.setInterval(function() {
22550                     _this.showPanelNext();
22551                 }, this.timer);
22552             }
22553
22554         }).createDelegate(this, [i, bullet], true));
22555                 
22556         
22557     },
22558      
22559     setActiveBullet : function(i)
22560     {
22561         if(Roo.isTouch){
22562             return;
22563         }
22564         
22565         Roo.each(this.el.select('.bullet', true).elements, function(el){
22566             el.removeClass('selected');
22567         });
22568
22569         var bullet = this.el.select('.bullet-' + i, true).first();
22570         
22571         if(!bullet){
22572             return;
22573         }
22574         
22575         bullet.addClass('selected');
22576     }
22577     
22578     
22579   
22580 });
22581
22582  
22583
22584  
22585  
22586 Roo.apply(Roo.bootstrap.TabGroup, {
22587     
22588     groups: {},
22589      /**
22590     * register a Navigation Group
22591     * @param {Roo.bootstrap.nav.Group} the navgroup to add
22592     */
22593     register : function(navgrp)
22594     {
22595         this.groups[navgrp.navId] = navgrp;
22596         
22597     },
22598     /**
22599     * fetch a Navigation Group based on the navigation ID
22600     * if one does not exist , it will get created.
22601     * @param {string} the navgroup to add
22602     * @returns {Roo.bootstrap.nav.Group} the navgroup 
22603     */
22604     get: function(navId) {
22605         if (typeof(this.groups[navId]) == 'undefined') {
22606             this.register(new Roo.bootstrap.TabGroup({ navId : navId }));
22607         }
22608         return this.groups[navId] ;
22609     }
22610     
22611     
22612     
22613 });
22614
22615  /*
22616  * - LGPL
22617  *
22618  * TabPanel
22619  * 
22620  */
22621
22622 /**
22623  * @class Roo.bootstrap.TabPanel
22624  * @extends Roo.bootstrap.Component
22625  * @children Roo.bootstrap.Component
22626  * Bootstrap TabPanel class
22627  * @cfg {Boolean} active panel active
22628  * @cfg {String} html panel content
22629  * @cfg {String} tabId  unique tab ID (will be autogenerated if not set. - used to match TabItem to Panel)
22630  * @cfg {String} navId The Roo.bootstrap.nav.Group which triggers show hide ()
22631  * @cfg {String} href click to link..
22632  * @cfg {Boolean} touchSlide if swiping slides tab to next panel (default off)
22633  * 
22634  * 
22635  * @constructor
22636  * Create a new TabPanel
22637  * @param {Object} config The config object
22638  */
22639
22640 Roo.bootstrap.TabPanel = function(config){
22641     Roo.bootstrap.TabPanel.superclass.constructor.call(this, config);
22642     this.addEvents({
22643         /**
22644              * @event changed
22645              * Fires when the active status changes
22646              * @param {Roo.bootstrap.TabPanel} this
22647              * @param {Boolean} state the new state
22648             
22649          */
22650         'changed': true,
22651         /**
22652              * @event beforedeactivate
22653              * Fires before a tab is de-activated - can be used to do validation on a form.
22654              * @param {Roo.bootstrap.TabPanel} this
22655              * @return {Boolean} false if there is an error
22656             
22657          */
22658         'beforedeactivate': true
22659      });
22660     
22661     this.tabId = this.tabId || Roo.id();
22662   
22663 };
22664
22665 Roo.extend(Roo.bootstrap.TabPanel, Roo.bootstrap.Component,  {
22666     
22667     active: false,
22668     html: false,
22669     tabId: false,
22670     navId : false,
22671     href : '',
22672     touchSlide : false,
22673     getAutoCreate : function(){
22674         
22675         
22676         var cfg = {
22677             tag: 'div',
22678             // item is needed for carousel - not sure if it has any effect otherwise
22679             cls: 'carousel-item tab-pane item' + ((this.href.length) ? ' clickable ' : ''),
22680             html: this.html || ''
22681         };
22682         
22683         if(this.active){
22684             cfg.cls += ' active';
22685         }
22686         
22687         if(this.tabId){
22688             cfg.tabId = this.tabId;
22689         }
22690         
22691         
22692         
22693         return cfg;
22694     },
22695     
22696     initEvents:  function()
22697     {
22698         var p = this.parent();
22699         
22700         this.navId = this.navId || p.navId;
22701         
22702         if (typeof(this.navId) != 'undefined') {
22703             // not really needed.. but just in case.. parent should be a NavGroup.
22704             var tg = Roo.bootstrap.TabGroup.get(this.navId);
22705             
22706             tg.register(this);
22707             
22708             var i = tg.tabs.length - 1;
22709             
22710             if(this.active && tg.bullets > 0 && i < tg.bullets){
22711                 tg.setActiveBullet(i);
22712             }
22713         }
22714         
22715         this.el.on('click', this.onClick, this);
22716         
22717         if(Roo.isTouch && this.touchSlide){
22718             this.el.on("touchstart", this.onTouchStart, this);
22719             this.el.on("touchmove", this.onTouchMove, this);
22720             this.el.on("touchend", this.onTouchEnd, this);
22721         }
22722         
22723     },
22724     
22725     onRender : function(ct, position)
22726     {
22727         Roo.bootstrap.TabPanel.superclass.onRender.call(this, ct, position);
22728     },
22729     
22730     setActive : function(state)
22731     {
22732         Roo.log("panel - set active " + this.tabId + "=" + state);
22733         
22734         this.active = state;
22735         if (!state) {
22736             this.el.removeClass('active');
22737             
22738         } else  if (!this.el.hasClass('active')) {
22739             this.el.addClass('active');
22740         }
22741         
22742         this.fireEvent('changed', this, state);
22743     },
22744     
22745     onClick : function(e)
22746     {
22747         e.preventDefault();
22748         
22749         if(!this.href.length){
22750             return;
22751         }
22752         
22753         window.location.href = this.href;
22754     },
22755     
22756     startX : 0,
22757     startY : 0,
22758     endX : 0,
22759     endY : 0,
22760     swiping : false,
22761     
22762     onTouchStart : function(e)
22763     {
22764         this.swiping = false;
22765         
22766         this.startX = e.browserEvent.touches[0].clientX;
22767         this.startY = e.browserEvent.touches[0].clientY;
22768     },
22769     
22770     onTouchMove : function(e)
22771     {
22772         this.swiping = true;
22773         
22774         this.endX = e.browserEvent.touches[0].clientX;
22775         this.endY = e.browserEvent.touches[0].clientY;
22776     },
22777     
22778     onTouchEnd : function(e)
22779     {
22780         if(!this.swiping){
22781             this.onClick(e);
22782             return;
22783         }
22784         
22785         var tabGroup = this.parent();
22786         
22787         if(this.endX > this.startX){ // swiping right
22788             tabGroup.showPanelPrev();
22789             return;
22790         }
22791         
22792         if(this.startX > this.endX){ // swiping left
22793             tabGroup.showPanelNext();
22794             return;
22795         }
22796     }
22797     
22798     
22799 });
22800  
22801
22802  
22803
22804  /*
22805  * - LGPL
22806  *
22807  * DateField
22808  * 
22809  */
22810
22811 /**
22812  * @class Roo.bootstrap.form.DateField
22813  * @extends Roo.bootstrap.form.Input
22814  * Bootstrap DateField class
22815  * @cfg {Number} weekStart default 0
22816  * @cfg {String} viewMode default empty, (months|years)
22817  * @cfg {String} minViewMode default empty, (months|years)
22818  * @cfg {Number} startDate default -Infinity
22819  * @cfg {Number} endDate default Infinity
22820  * @cfg {Boolean} todayHighlight default false
22821  * @cfg {Boolean} todayBtn default false
22822  * @cfg {Boolean} calendarWeeks default false
22823  * @cfg {Object} daysOfWeekDisabled default empty
22824  * @cfg {Boolean} singleMode default false (true | false)
22825  * 
22826  * @cfg {Boolean} keyboardNavigation default true
22827  * @cfg {String} language default en
22828  * 
22829  * @constructor
22830  * Create a new DateField
22831  * @param {Object} config The config object
22832  */
22833
22834 Roo.bootstrap.form.DateField = function(config){
22835     Roo.bootstrap.form.DateField.superclass.constructor.call(this, config);
22836      this.addEvents({
22837             /**
22838              * @event show
22839              * Fires when this field show.
22840              * @param {Roo.bootstrap.form.DateField} this
22841              * @param {Mixed} date The date value
22842              */
22843             show : true,
22844             /**
22845              * @event show
22846              * Fires when this field hide.
22847              * @param {Roo.bootstrap.form.DateField} this
22848              * @param {Mixed} date The date value
22849              */
22850             hide : true,
22851             /**
22852              * @event select
22853              * Fires when select a date.
22854              * @param {Roo.bootstrap.form.DateField} this
22855              * @param {Mixed} date The date value
22856              */
22857             select : true,
22858             /**
22859              * @event beforeselect
22860              * Fires when before select a date.
22861              * @param {Roo.bootstrap.form.DateField} this
22862              * @param {Mixed} date The date value
22863              */
22864             beforeselect : true
22865         });
22866 };
22867
22868 Roo.extend(Roo.bootstrap.form.DateField, Roo.bootstrap.form.Input,  {
22869     
22870     /**
22871      * @cfg {String} format
22872      * The default date format string which can be overriden for localization support.  The format must be
22873      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
22874      */
22875     format : "m/d/y",
22876     /**
22877      * @cfg {String} altFormats
22878      * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
22879      * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
22880      */
22881     altFormats : "m/d/Y|m-d-y|m-d-Y|m/d|m-d|md|mdy|mdY|d",
22882     
22883     weekStart : 0,
22884     
22885     viewMode : '',
22886     
22887     minViewMode : '',
22888     
22889     todayHighlight : false,
22890     
22891     todayBtn: false,
22892     
22893     language: 'en',
22894     
22895     keyboardNavigation: true,
22896     
22897     calendarWeeks: false,
22898     
22899     startDate: -Infinity,
22900     
22901     endDate: Infinity,
22902     
22903     daysOfWeekDisabled: [],
22904     
22905     _events: [],
22906     
22907     singleMode : false,
22908     
22909     UTCDate: function()
22910     {
22911         return new Date(Date.UTC.apply(Date, arguments));
22912     },
22913     
22914     UTCToday: function()
22915     {
22916         var today = new Date();
22917         return this.UTCDate(today.getUTCFullYear(), today.getUTCMonth(), today.getUTCDate());
22918     },
22919     
22920     getDate: function() {
22921             var d = this.getUTCDate();
22922             return new Date(d.getTime() + (d.getTimezoneOffset()*60000));
22923     },
22924     
22925     getUTCDate: function() {
22926             return this.date;
22927     },
22928     
22929     setDate: function(d) {
22930             this.setUTCDate(new Date(d.getTime() - (d.getTimezoneOffset()*60000)));
22931     },
22932     
22933     setUTCDate: function(d) {
22934             this.date = d;
22935             this.setValue(this.formatDate(this.date));
22936     },
22937         
22938     onRender: function(ct, position)
22939     {
22940         
22941         Roo.bootstrap.form.DateField.superclass.onRender.call(this, ct, position);
22942         
22943         this.language = this.language || 'en';
22944         this.language = this.language in Roo.bootstrap.form.DateField.dates ? this.language : this.language.split('-')[0];
22945         this.language = this.language in Roo.bootstrap.form.DateField.dates ? this.language : "en";
22946         
22947         this.isRTL = Roo.bootstrap.form.DateField.dates[this.language].rtl || false;
22948         this.format = this.format || 'm/d/y';
22949         this.isInline = false;
22950         this.isInput = true;
22951         this.component = this.el.select('.add-on', true).first() || false;
22952         this.component = (this.component && this.component.length === 0) ? false : this.component;
22953         this.hasInput = this.component && this.inputEl().length;
22954         
22955         if (typeof(this.minViewMode === 'string')) {
22956             switch (this.minViewMode) {
22957                 case 'months':
22958                     this.minViewMode = 1;
22959                     break;
22960                 case 'years':
22961                     this.minViewMode = 2;
22962                     break;
22963                 default:
22964                     this.minViewMode = 0;
22965                     break;
22966             }
22967         }
22968         
22969         if (typeof(this.viewMode === 'string')) {
22970             switch (this.viewMode) {
22971                 case 'months':
22972                     this.viewMode = 1;
22973                     break;
22974                 case 'years':
22975                     this.viewMode = 2;
22976                     break;
22977                 default:
22978                     this.viewMode = 0;
22979                     break;
22980             }
22981         }
22982                 
22983         this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.form.DateField.template);
22984         
22985 //        this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.form.DateField.template);
22986         
22987         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
22988         
22989         this.picker().on('mousedown', this.onMousedown, this);
22990         this.picker().on('click', this.onClick, this);
22991         
22992         this.picker().addClass('datepicker-dropdown');
22993         
22994         this.startViewMode = this.viewMode;
22995         
22996         if(this.singleMode){
22997             Roo.each(this.picker().select('thead > tr > th', true).elements, function(v){
22998                 v.setVisibilityMode(Roo.Element.DISPLAY);
22999                 v.hide();
23000             });
23001             
23002             Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
23003                 v.setStyle('width', '189px');
23004             });
23005         }
23006         
23007         Roo.each(this.picker().select('tfoot th.today', true).elements, function(v){
23008             if(!this.calendarWeeks){
23009                 v.remove();
23010                 return;
23011             }
23012             
23013             v.dom.innerHTML = Roo.bootstrap.form.DateField.dates[this.language].today;
23014             v.attr('colspan', function(i, val){
23015                 return parseInt(val) + 1;
23016             });
23017         });
23018                         
23019         
23020         this.weekEnd = this.weekStart === 0 ? 6 : this.weekStart - 1;
23021         
23022         this.setStartDate(this.startDate);
23023         this.setEndDate(this.endDate);
23024         
23025         this.setDaysOfWeekDisabled(this.daysOfWeekDisabled);
23026         
23027         this.fillDow();
23028         this.fillMonths();
23029         this.update();
23030         this.showMode();
23031         
23032         if(this.isInline) {
23033             this.showPopup();
23034         }
23035     },
23036     
23037     picker : function()
23038     {
23039         return this.pickerEl;
23040 //        return this.el.select('.datepicker', true).first();
23041     },
23042     
23043     fillDow: function()
23044     {
23045         var dowCnt = this.weekStart;
23046         
23047         var dow = {
23048             tag: 'tr',
23049             cn: [
23050                 
23051             ]
23052         };
23053         
23054         if(this.calendarWeeks){
23055             dow.cn.push({
23056                 tag: 'th',
23057                 cls: 'cw',
23058                 html: '&nbsp;'
23059             })
23060         }
23061         
23062         while (dowCnt < this.weekStart + 7) {
23063             dow.cn.push({
23064                 tag: 'th',
23065                 cls: 'dow',
23066                 html: Roo.bootstrap.form.DateField.dates[this.language].daysMin[(dowCnt++)%7]
23067             });
23068         }
23069         
23070         this.picker().select('>.datepicker-days thead', true).first().createChild(dow);
23071     },
23072     
23073     fillMonths: function()
23074     {    
23075         var i = 0;
23076         var months = this.picker().select('>.datepicker-months td', true).first();
23077         
23078         months.dom.innerHTML = '';
23079         
23080         while (i < 12) {
23081             var month = {
23082                 tag: 'span',
23083                 cls: 'month',
23084                 html: Roo.bootstrap.form.DateField.dates[this.language].monthsShort[i++]
23085             };
23086             
23087             months.createChild(month);
23088         }
23089         
23090     },
23091     
23092     update: function()
23093     {
23094         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;
23095         
23096         if (this.date < this.startDate) {
23097             this.viewDate = new Date(this.startDate);
23098         } else if (this.date > this.endDate) {
23099             this.viewDate = new Date(this.endDate);
23100         } else {
23101             this.viewDate = new Date(this.date);
23102         }
23103         
23104         this.fill();
23105     },
23106     
23107     fill: function() 
23108     {
23109         var d = new Date(this.viewDate),
23110                 year = d.getUTCFullYear(),
23111                 month = d.getUTCMonth(),
23112                 startYear = this.startDate !== -Infinity ? this.startDate.getUTCFullYear() : -Infinity,
23113                 startMonth = this.startDate !== -Infinity ? this.startDate.getUTCMonth() : -Infinity,
23114                 endYear = this.endDate !== Infinity ? this.endDate.getUTCFullYear() : Infinity,
23115                 endMonth = this.endDate !== Infinity ? this.endDate.getUTCMonth() : Infinity,
23116                 currentDate = this.date && this.date.valueOf(),
23117                 today = this.UTCToday();
23118         
23119         this.picker().select('>.datepicker-days thead th.switch', true).first().dom.innerHTML = Roo.bootstrap.form.DateField.dates[this.language].months[month]+' '+year;
23120         
23121 //        this.picker().select('>tfoot th.today', true).first().dom.innerHTML = Roo.bootstrap.form.DateField.dates[this.language].today;
23122         
23123 //        this.picker.select('>tfoot th.today').
23124 //                                              .text(dates[this.language].today)
23125 //                                              .toggle(this.todayBtn !== false);
23126     
23127         this.updateNavArrows();
23128         this.fillMonths();
23129                                                 
23130         var prevMonth = this.UTCDate(year, month-1, 28,0,0,0,0),
23131         
23132         day = prevMonth.getDaysInMonth(prevMonth.getUTCFullYear(), prevMonth.getUTCMonth());
23133          
23134         prevMonth.setUTCDate(day);
23135         
23136         prevMonth.setUTCDate(day - (prevMonth.getUTCDay() - this.weekStart + 7)%7);
23137         
23138         var nextMonth = new Date(prevMonth);
23139         
23140         nextMonth.setUTCDate(nextMonth.getUTCDate() + 42);
23141         
23142         nextMonth = nextMonth.valueOf();
23143         
23144         var fillMonths = false;
23145         
23146         this.picker().select('>.datepicker-days tbody',true).first().dom.innerHTML = '';
23147         
23148         while(prevMonth.valueOf() <= nextMonth) {
23149             var clsName = '';
23150             
23151             if (prevMonth.getUTCDay() === this.weekStart) {
23152                 if(fillMonths){
23153                     this.picker().select('>.datepicker-days tbody',true).first().createChild(fillMonths);
23154                 }
23155                     
23156                 fillMonths = {
23157                     tag: 'tr',
23158                     cn: []
23159                 };
23160                 
23161                 if(this.calendarWeeks){
23162                     // ISO 8601: First week contains first thursday.
23163                     // ISO also states week starts on Monday, but we can be more abstract here.
23164                     var
23165                     // Start of current week: based on weekstart/current date
23166                     ws = new Date(+prevMonth + (this.weekStart - prevMonth.getUTCDay() - 7) % 7 * 864e5),
23167                     // Thursday of this week
23168                     th = new Date(+ws + (7 + 4 - ws.getUTCDay()) % 7 * 864e5),
23169                     // First Thursday of year, year from thursday
23170                     yth = new Date(+(yth = this.UTCDate(th.getUTCFullYear(), 0, 1)) + (7 + 4 - yth.getUTCDay())%7*864e5),
23171                     // Calendar week: ms between thursdays, div ms per day, div 7 days
23172                     calWeek =  (th - yth) / 864e5 / 7 + 1;
23173                     
23174                     fillMonths.cn.push({
23175                         tag: 'td',
23176                         cls: 'cw',
23177                         html: calWeek
23178                     });
23179                 }
23180             }
23181             
23182             if (prevMonth.getUTCFullYear() < year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() < month)) {
23183                 clsName += ' old';
23184             } else if (prevMonth.getUTCFullYear() > year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() > month)) {
23185                 clsName += ' new';
23186             }
23187             if (this.todayHighlight &&
23188                 prevMonth.getUTCFullYear() == today.getFullYear() &&
23189                 prevMonth.getUTCMonth() == today.getMonth() &&
23190                 prevMonth.getUTCDate() == today.getDate()) {
23191                 clsName += ' today';
23192             }
23193             
23194             if (currentDate && prevMonth.valueOf() === currentDate) {
23195                 clsName += ' active';
23196             }
23197             
23198             if (prevMonth.valueOf() < this.startDate || prevMonth.valueOf() > this.endDate ||
23199                     this.daysOfWeekDisabled.indexOf(prevMonth.getUTCDay()) !== -1) {
23200                     clsName += ' disabled';
23201             }
23202             
23203             fillMonths.cn.push({
23204                 tag: 'td',
23205                 cls: 'day ' + clsName,
23206                 html: prevMonth.getDate()
23207             });
23208             
23209             prevMonth.setDate(prevMonth.getDate()+1);
23210         }
23211           
23212         var currentYear = this.date && this.date.getUTCFullYear();
23213         var currentMonth = this.date && this.date.getUTCMonth();
23214         
23215         this.picker().select('>.datepicker-months th.switch',true).first().dom.innerHTML = year;
23216         
23217         Roo.each(this.picker().select('>.datepicker-months tbody span',true).elements, function(v,k){
23218             v.removeClass('active');
23219             
23220             if(currentYear === year && k === currentMonth){
23221                 v.addClass('active');
23222             }
23223             
23224             if (year < startYear || year > endYear || (year == startYear && k < startMonth) || (year == endYear && k > endMonth)) {
23225                 v.addClass('disabled');
23226             }
23227             
23228         });
23229         
23230         
23231         year = parseInt(year/10, 10) * 10;
23232         
23233         this.picker().select('>.datepicker-years th.switch', true).first().dom.innerHTML = year + '-' + (year + 9);
23234         
23235         this.picker().select('>.datepicker-years tbody td',true).first().dom.innerHTML = '';
23236         
23237         year -= 1;
23238         for (var i = -1; i < 11; i++) {
23239             this.picker().select('>.datepicker-years tbody td',true).first().createChild({
23240                 tag: 'span',
23241                 cls: 'year' + (i === -1 || i === 10 ? ' old' : '') + (currentYear === year ? ' active' : '') + (year < startYear || year > endYear ? ' disabled' : ''),
23242                 html: year
23243             });
23244             
23245             year += 1;
23246         }
23247     },
23248     
23249     showMode: function(dir) 
23250     {
23251         if (dir) {
23252             this.viewMode = Math.max(this.minViewMode, Math.min(2, this.viewMode + dir));
23253         }
23254         
23255         Roo.each(this.picker().select('>div',true).elements, function(v){
23256             v.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
23257             v.hide();
23258         });
23259         this.picker().select('>.datepicker-'+Roo.bootstrap.form.DateField.modes[this.viewMode].clsName, true).first().show();
23260     },
23261     
23262     place: function()
23263     {
23264         if(this.isInline) {
23265             return;
23266         }
23267         
23268         this.picker().removeClass(['bottom', 'top']);
23269         
23270         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
23271             /*
23272              * place to the top of element!
23273              *
23274              */
23275             
23276             this.picker().addClass('top');
23277             this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
23278             
23279             return;
23280         }
23281         
23282         this.picker().addClass('bottom');
23283         
23284         this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
23285     },
23286     
23287     parseDate : function(value)
23288     {
23289         if(!value || value instanceof Date){
23290             return value;
23291         }
23292         var v = Date.parseDate(value, this.format);
23293         if (!v && (this.useIso || value.match(/^(\d{4})-0?(\d+)-0?(\d+)/))) {
23294             v = Date.parseDate(value, 'Y-m-d');
23295         }
23296         if(!v && this.altFormats){
23297             if(!this.altFormatsArray){
23298                 this.altFormatsArray = this.altFormats.split("|");
23299             }
23300             for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
23301                 v = Date.parseDate(value, this.altFormatsArray[i]);
23302             }
23303         }
23304         return v;
23305     },
23306     
23307     formatDate : function(date, fmt)
23308     {   
23309         return (!date || !(date instanceof Date)) ?
23310         date : date.dateFormat(fmt || this.format);
23311     },
23312     
23313     onFocus : function()
23314     {
23315         Roo.bootstrap.form.DateField.superclass.onFocus.call(this);
23316         this.showPopup();
23317     },
23318     
23319     onBlur : function()
23320     {
23321         Roo.bootstrap.form.DateField.superclass.onBlur.call(this);
23322         
23323         var d = this.inputEl().getValue();
23324         
23325         this.setValue(d);
23326                 
23327         this.hidePopup();
23328     },
23329     
23330     showPopup : function()
23331     {
23332         this.picker().show();
23333         this.update();
23334         this.place();
23335         
23336         this.fireEvent('showpopup', this, this.date);
23337     },
23338     
23339     hidePopup : function()
23340     {
23341         if(this.isInline) {
23342             return;
23343         }
23344         this.picker().hide();
23345         this.viewMode = this.startViewMode;
23346         this.showMode();
23347         
23348         this.fireEvent('hidepopup', this, this.date);
23349         
23350     },
23351     
23352     onMousedown: function(e)
23353     {
23354         e.stopPropagation();
23355         e.preventDefault();
23356     },
23357     
23358     keyup: function(e)
23359     {
23360         Roo.bootstrap.form.DateField.superclass.keyup.call(this);
23361         this.update();
23362     },
23363
23364     setValue: function(v)
23365     {
23366         if(this.fireEvent('beforeselect', this, v) !== false){
23367             var d = new Date(this.parseDate(v) ).clearTime();
23368         
23369             if(isNaN(d.getTime())){
23370                 this.date = this.viewDate = '';
23371                 Roo.bootstrap.form.DateField.superclass.setValue.call(this, '');
23372                 return;
23373             }
23374
23375             v = this.formatDate(d);
23376
23377             Roo.bootstrap.form.DateField.superclass.setValue.call(this, v);
23378
23379             this.date = new Date(d.getTime() - d.getTimezoneOffset()*60000);
23380
23381             this.update();
23382
23383             this.fireEvent('select', this, this.date);
23384         }
23385     },
23386     
23387     getValue: function()
23388     {
23389         return this.formatDate(this.date);
23390     },
23391     
23392     fireKey: function(e)
23393     {
23394         if (!this.picker().isVisible()){
23395             if (e.keyCode == 27) { // allow escape to hide and re-show picker
23396                 this.showPopup();
23397             }
23398             return;
23399         }
23400         
23401         var dateChanged = false,
23402         dir, day, month,
23403         newDate, newViewDate;
23404         
23405         switch(e.keyCode){
23406             case 27: // escape
23407                 this.hidePopup();
23408                 e.preventDefault();
23409                 break;
23410             case 37: // left
23411             case 39: // right
23412                 if (!this.keyboardNavigation) {
23413                     break;
23414                 }
23415                 dir = e.keyCode == 37 ? -1 : 1;
23416                 
23417                 if (e.ctrlKey){
23418                     newDate = this.moveYear(this.date, dir);
23419                     newViewDate = this.moveYear(this.viewDate, dir);
23420                 } else if (e.shiftKey){
23421                     newDate = this.moveMonth(this.date, dir);
23422                     newViewDate = this.moveMonth(this.viewDate, dir);
23423                 } else {
23424                     newDate = new Date(this.date);
23425                     newDate.setUTCDate(this.date.getUTCDate() + dir);
23426                     newViewDate = new Date(this.viewDate);
23427                     newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir);
23428                 }
23429                 if (this.dateWithinRange(newDate)){
23430                     this.date = newDate;
23431                     this.viewDate = newViewDate;
23432                     this.setValue(this.formatDate(this.date));
23433 //                    this.update();
23434                     e.preventDefault();
23435                     dateChanged = true;
23436                 }
23437                 break;
23438             case 38: // up
23439             case 40: // down
23440                 if (!this.keyboardNavigation) {
23441                     break;
23442                 }
23443                 dir = e.keyCode == 38 ? -1 : 1;
23444                 if (e.ctrlKey){
23445                     newDate = this.moveYear(this.date, dir);
23446                     newViewDate = this.moveYear(this.viewDate, dir);
23447                 } else if (e.shiftKey){
23448                     newDate = this.moveMonth(this.date, dir);
23449                     newViewDate = this.moveMonth(this.viewDate, dir);
23450                 } else {
23451                     newDate = new Date(this.date);
23452                     newDate.setUTCDate(this.date.getUTCDate() + dir * 7);
23453                     newViewDate = new Date(this.viewDate);
23454                     newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir * 7);
23455                 }
23456                 if (this.dateWithinRange(newDate)){
23457                     this.date = newDate;
23458                     this.viewDate = newViewDate;
23459                     this.setValue(this.formatDate(this.date));
23460 //                    this.update();
23461                     e.preventDefault();
23462                     dateChanged = true;
23463                 }
23464                 break;
23465             case 13: // enter
23466                 this.setValue(this.formatDate(this.date));
23467                 this.hidePopup();
23468                 e.preventDefault();
23469                 break;
23470             case 9: // tab
23471                 this.setValue(this.formatDate(this.date));
23472                 this.hidePopup();
23473                 break;
23474             case 16: // shift
23475             case 17: // ctrl
23476             case 18: // alt
23477                 break;
23478             default :
23479                 this.hidePopup();
23480                 
23481         }
23482     },
23483     
23484     
23485     onClick: function(e) 
23486     {
23487         e.stopPropagation();
23488         e.preventDefault();
23489         
23490         var target = e.getTarget();
23491         
23492         if(target.nodeName.toLowerCase() === 'i'){
23493             target = Roo.get(target).dom.parentNode;
23494         }
23495         
23496         var nodeName = target.nodeName;
23497         var className = target.className;
23498         var html = target.innerHTML;
23499         //Roo.log(nodeName);
23500         
23501         switch(nodeName.toLowerCase()) {
23502             case 'th':
23503                 switch(className) {
23504                     case 'switch':
23505                         this.showMode(1);
23506                         break;
23507                     case 'prev':
23508                     case 'next':
23509                         var dir = Roo.bootstrap.form.DateField.modes[this.viewMode].navStep * (className == 'prev' ? -1 : 1);
23510                         switch(this.viewMode){
23511                                 case 0:
23512                                         this.viewDate = this.moveMonth(this.viewDate, dir);
23513                                         break;
23514                                 case 1:
23515                                 case 2:
23516                                         this.viewDate = this.moveYear(this.viewDate, dir);
23517                                         break;
23518                         }
23519                         this.fill();
23520                         break;
23521                     case 'today':
23522                         var date = new Date();
23523                         this.date = this.UTCDate(date.getFullYear(), date.getMonth(), date.getDate(), 0, 0, 0);
23524 //                        this.fill()
23525                         this.setValue(this.formatDate(this.date));
23526                         
23527                         this.hidePopup();
23528                         break;
23529                 }
23530                 break;
23531             case 'span':
23532                 if (className.indexOf('disabled') < 0) {
23533                 if (!this.viewDate) {
23534                     this.viewDate = new Date();
23535                 }
23536                 this.viewDate.setUTCDate(1);
23537                     if (className.indexOf('month') > -1) {
23538                         this.viewDate.setUTCMonth(Roo.bootstrap.form.DateField.dates[this.language].monthsShort.indexOf(html));
23539                     } else {
23540                         var year = parseInt(html, 10) || 0;
23541                         this.viewDate.setUTCFullYear(year);
23542                         
23543                     }
23544                     
23545                     if(this.singleMode){
23546                         this.setValue(this.formatDate(this.viewDate));
23547                         this.hidePopup();
23548                         return;
23549                     }
23550                     
23551                     this.showMode(-1);
23552                     this.fill();
23553                 }
23554                 break;
23555                 
23556             case 'td':
23557                 //Roo.log(className);
23558                 if (className.indexOf('day') > -1 && className.indexOf('disabled') < 0 ){
23559                     var day = parseInt(html, 10) || 1;
23560                     var year =  (this.viewDate || new Date()).getUTCFullYear(),
23561                         month = (this.viewDate || new Date()).getUTCMonth();
23562
23563                     if (className.indexOf('old') > -1) {
23564                         if(month === 0 ){
23565                             month = 11;
23566                             year -= 1;
23567                         }else{
23568                             month -= 1;
23569                         }
23570                     } else if (className.indexOf('new') > -1) {
23571                         if (month == 11) {
23572                             month = 0;
23573                             year += 1;
23574                         } else {
23575                             month += 1;
23576                         }
23577                     }
23578                     //Roo.log([year,month,day]);
23579                     this.date = this.UTCDate(year, month, day,0,0,0,0);
23580                     this.viewDate = this.UTCDate(year, month, Math.min(28, day),0,0,0,0);
23581 //                    this.fill();
23582                     //Roo.log(this.formatDate(this.date));
23583                     this.setValue(this.formatDate(this.date));
23584                     this.hidePopup();
23585                 }
23586                 break;
23587         }
23588     },
23589     
23590     setStartDate: function(startDate)
23591     {
23592         this.startDate = startDate || -Infinity;
23593         if (this.startDate !== -Infinity) {
23594             this.startDate = this.parseDate(this.startDate);
23595         }
23596         this.update();
23597         this.updateNavArrows();
23598     },
23599
23600     setEndDate: function(endDate)
23601     {
23602         this.endDate = endDate || Infinity;
23603         if (this.endDate !== Infinity) {
23604             this.endDate = this.parseDate(this.endDate);
23605         }
23606         this.update();
23607         this.updateNavArrows();
23608     },
23609     
23610     setDaysOfWeekDisabled: function(daysOfWeekDisabled)
23611     {
23612         this.daysOfWeekDisabled = daysOfWeekDisabled || [];
23613         if (typeof(this.daysOfWeekDisabled) !== 'object') {
23614             this.daysOfWeekDisabled = this.daysOfWeekDisabled.split(/,\s*/);
23615         }
23616         this.daysOfWeekDisabled = this.daysOfWeekDisabled.map(function (d) {
23617             return parseInt(d, 10);
23618         });
23619         this.update();
23620         this.updateNavArrows();
23621     },
23622     
23623     updateNavArrows: function() 
23624     {
23625         if(this.singleMode){
23626             return;
23627         }
23628         
23629         var d = new Date(this.viewDate),
23630         year = d.getUTCFullYear(),
23631         month = d.getUTCMonth();
23632         
23633         Roo.each(this.picker().select('.prev', true).elements, function(v){
23634             v.show();
23635             switch (this.viewMode) {
23636                 case 0:
23637
23638                     if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear() && month <= this.startDate.getUTCMonth()) {
23639                         v.hide();
23640                     }
23641                     break;
23642                 case 1:
23643                 case 2:
23644                     if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear()) {
23645                         v.hide();
23646                     }
23647                     break;
23648             }
23649         });
23650         
23651         Roo.each(this.picker().select('.next', true).elements, function(v){
23652             v.show();
23653             switch (this.viewMode) {
23654                 case 0:
23655
23656                     if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear() && month >= this.endDate.getUTCMonth()) {
23657                         v.hide();
23658                     }
23659                     break;
23660                 case 1:
23661                 case 2:
23662                     if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear()) {
23663                         v.hide();
23664                     }
23665                     break;
23666             }
23667         })
23668     },
23669     
23670     moveMonth: function(date, dir)
23671     {
23672         if (!dir) {
23673             return date;
23674         }
23675         var new_date = new Date(date.valueOf()),
23676         day = new_date.getUTCDate(),
23677         month = new_date.getUTCMonth(),
23678         mag = Math.abs(dir),
23679         new_month, test;
23680         dir = dir > 0 ? 1 : -1;
23681         if (mag == 1){
23682             test = dir == -1
23683             // If going back one month, make sure month is not current month
23684             // (eg, Mar 31 -> Feb 31 == Feb 28, not Mar 02)
23685             ? function(){
23686                 return new_date.getUTCMonth() == month;
23687             }
23688             // If going forward one month, make sure month is as expected
23689             // (eg, Jan 31 -> Feb 31 == Feb 28, not Mar 02)
23690             : function(){
23691                 return new_date.getUTCMonth() != new_month;
23692             };
23693             new_month = month + dir;
23694             new_date.setUTCMonth(new_month);
23695             // Dec -> Jan (12) or Jan -> Dec (-1) -- limit expected date to 0-11
23696             if (new_month < 0 || new_month > 11) {
23697                 new_month = (new_month + 12) % 12;
23698             }
23699         } else {
23700             // For magnitudes >1, move one month at a time...
23701             for (var i=0; i<mag; i++) {
23702                 // ...which might decrease the day (eg, Jan 31 to Feb 28, etc)...
23703                 new_date = this.moveMonth(new_date, dir);
23704             }
23705             // ...then reset the day, keeping it in the new month
23706             new_month = new_date.getUTCMonth();
23707             new_date.setUTCDate(day);
23708             test = function(){
23709                 return new_month != new_date.getUTCMonth();
23710             };
23711         }
23712         // Common date-resetting loop -- if date is beyond end of month, make it
23713         // end of month
23714         while (test()){
23715             new_date.setUTCDate(--day);
23716             new_date.setUTCMonth(new_month);
23717         }
23718         return new_date;
23719     },
23720
23721     moveYear: function(date, dir)
23722     {
23723         return this.moveMonth(date, dir*12);
23724     },
23725
23726     dateWithinRange: function(date)
23727     {
23728         return date >= this.startDate && date <= this.endDate;
23729     },
23730
23731     
23732     remove: function() 
23733     {
23734         this.picker().remove();
23735     },
23736     
23737     validateValue : function(value)
23738     {
23739         if(this.getVisibilityEl().hasClass('hidden')){
23740             return true;
23741         }
23742         
23743         if(value.length < 1)  {
23744             if(this.allowBlank){
23745                 return true;
23746             }
23747             return false;
23748         }
23749         
23750         if(value.length < this.minLength){
23751             return false;
23752         }
23753         if(value.length > this.maxLength){
23754             return false;
23755         }
23756         if(this.vtype){
23757             var vt = Roo.form.VTypes;
23758             if(!vt[this.vtype](value, this)){
23759                 return false;
23760             }
23761         }
23762         if(typeof this.validator == "function"){
23763             var msg = this.validator(value);
23764             if(msg !== true){
23765                 return false;
23766             }
23767         }
23768         
23769         if(this.regex && !this.regex.test(value)){
23770             return false;
23771         }
23772         
23773         if(typeof(this.parseDate(value)) == 'undefined'){
23774             return false;
23775         }
23776         
23777         if (this.endDate !== Infinity && this.parseDate(value).getTime() > this.endDate.getTime()) {
23778             return false;
23779         }      
23780         
23781         if (this.startDate !== -Infinity && this.parseDate(value).getTime() < this.startDate.getTime()) {
23782             return false;
23783         } 
23784         
23785         
23786         return true;
23787     },
23788     
23789     reset : function()
23790     {
23791         this.date = this.viewDate = '';
23792         
23793         Roo.bootstrap.form.DateField.superclass.setValue.call(this, '');
23794     }
23795    
23796 });
23797
23798 Roo.apply(Roo.bootstrap.form.DateField,  {
23799     
23800     head : {
23801         tag: 'thead',
23802         cn: [
23803         {
23804             tag: 'tr',
23805             cn: [
23806             {
23807                 tag: 'th',
23808                 cls: 'prev',
23809                 html: '<i class="fa fa-arrow-left"/>'
23810             },
23811             {
23812                 tag: 'th',
23813                 cls: 'switch',
23814                 colspan: '5'
23815             },
23816             {
23817                 tag: 'th',
23818                 cls: 'next',
23819                 html: '<i class="fa fa-arrow-right"/>'
23820             }
23821
23822             ]
23823         }
23824         ]
23825     },
23826     
23827     content : {
23828         tag: 'tbody',
23829         cn: [
23830         {
23831             tag: 'tr',
23832             cn: [
23833             {
23834                 tag: 'td',
23835                 colspan: '7'
23836             }
23837             ]
23838         }
23839         ]
23840     },
23841     
23842     footer : {
23843         tag: 'tfoot',
23844         cn: [
23845         {
23846             tag: 'tr',
23847             cn: [
23848             {
23849                 tag: 'th',
23850                 colspan: '7',
23851                 cls: 'today'
23852             }
23853                     
23854             ]
23855         }
23856         ]
23857     },
23858     
23859     dates:{
23860         en: {
23861             days: ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday"],
23862             daysShort: ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"],
23863             daysMin: ["Su", "Mo", "Tu", "We", "Th", "Fr", "Sa", "Su"],
23864             months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
23865             monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"],
23866             today: "Today"
23867         }
23868     },
23869     
23870     modes: [
23871     {
23872         clsName: 'days',
23873         navFnc: 'Month',
23874         navStep: 1
23875     },
23876     {
23877         clsName: 'months',
23878         navFnc: 'FullYear',
23879         navStep: 1
23880     },
23881     {
23882         clsName: 'years',
23883         navFnc: 'FullYear',
23884         navStep: 10
23885     }]
23886 });
23887
23888 Roo.apply(Roo.bootstrap.form.DateField,  {
23889   
23890     template : {
23891         tag: 'div',
23892         cls: 'datepicker dropdown-menu roo-dynamic shadow',
23893         cn: [
23894         {
23895             tag: 'div',
23896             cls: 'datepicker-days',
23897             cn: [
23898             {
23899                 tag: 'table',
23900                 cls: 'table-condensed',
23901                 cn:[
23902                 Roo.bootstrap.form.DateField.head,
23903                 {
23904                     tag: 'tbody'
23905                 },
23906                 Roo.bootstrap.form.DateField.footer
23907                 ]
23908             }
23909             ]
23910         },
23911         {
23912             tag: 'div',
23913             cls: 'datepicker-months',
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             tag: 'div',
23928             cls: 'datepicker-years',
23929             cn: [
23930             {
23931                 tag: 'table',
23932                 cls: 'table-condensed',
23933                 cn:[
23934                 Roo.bootstrap.form.DateField.head,
23935                 Roo.bootstrap.form.DateField.content,
23936                 Roo.bootstrap.form.DateField.footer
23937                 ]
23938             }
23939             ]
23940         }
23941         ]
23942     }
23943 });
23944
23945  
23946
23947  /*
23948  * - LGPL
23949  *
23950  * TimeField
23951  * 
23952  */
23953
23954 /**
23955  * @class Roo.bootstrap.form.TimeField
23956  * @extends Roo.bootstrap.form.Input
23957  * Bootstrap DateField class
23958  * @cfg {Number} minuteStep the minutes is always the multiple of a fixed number, default 1
23959  * 
23960  * 
23961  * @constructor
23962  * Create a new TimeField
23963  * @param {Object} config The config object
23964  */
23965
23966 Roo.bootstrap.form.TimeField = function(config){
23967     Roo.bootstrap.form.TimeField.superclass.constructor.call(this, config);
23968     this.addEvents({
23969             /**
23970              * @event show
23971              * Fires when this field show.
23972              * @param {Roo.bootstrap.form.DateField} thisthis
23973              * @param {Mixed} date The date value
23974              */
23975             show : true,
23976             /**
23977              * @event show
23978              * Fires when this field hide.
23979              * @param {Roo.bootstrap.form.DateField} this
23980              * @param {Mixed} date The date value
23981              */
23982             hide : true,
23983             /**
23984              * @event select
23985              * Fires when select a date.
23986              * @param {Roo.bootstrap.form.DateField} this
23987              * @param {Mixed} date The date value
23988              */
23989             select : true
23990         });
23991 };
23992
23993 Roo.extend(Roo.bootstrap.form.TimeField, Roo.bootstrap.form.Input,  {
23994     
23995     /**
23996      * @cfg {String} format
23997      * The default time format string which can be overriden for localization support.  The format must be
23998      * valid according to {@link Date#parseDate} (defaults to 'H:i').
23999      */
24000     format : "H:i",
24001     minuteStep : 1,
24002
24003     getAutoCreate : function()
24004     {
24005         this.after = '<i class="fa far fa-clock"></i>';
24006         return Roo.bootstrap.form.TimeField.superclass.getAutoCreate.call(this);
24007         
24008          
24009     },
24010     onRender: function(ct, position)
24011     {
24012         
24013         Roo.bootstrap.form.TimeField.superclass.onRender.call(this, ct, position);
24014                 
24015         this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.form.TimeField.template);
24016         
24017         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
24018         
24019         this.pop = this.picker().select('>.datepicker-time',true).first();
24020         this.pop.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
24021         
24022         this.picker().on('mousedown', this.onMousedown, this);
24023         this.picker().on('click', this.onClick, this);
24024         
24025         this.picker().addClass('datepicker-dropdown');
24026     
24027         this.fillTime();
24028         this.update();
24029             
24030         this.pop.select('.hours-up', true).first().on('click', this.onIncrementHours, this);
24031         this.pop.select('.hours-down', true).first().on('click', this.onDecrementHours, this);
24032         this.pop.select('.minutes-up', true).first().on('click', this.onIncrementMinutes, this);
24033         this.pop.select('.minutes-down', true).first().on('click', this.onDecrementMinutes, this);
24034         this.pop.select('button.period', true).first().on('click', this.onTogglePeriod, this);
24035         this.pop.select('button.ok', true).first().on('click', this.setTime, this);
24036
24037     },
24038     
24039     fireKey: function(e){
24040         if (!this.picker().isVisible()){
24041             if (e.keyCode == 27) { // allow escape to hide and re-show picker
24042                 this.show();
24043             }
24044             return;
24045         }
24046
24047         e.preventDefault();
24048         
24049         switch(e.keyCode){
24050             case 27: // escape
24051                 this.hide();
24052                 break;
24053             case 37: // left
24054             case 39: // right
24055                 this.onTogglePeriod();
24056                 break;
24057             case 38: // up
24058                 this.onIncrementMinutes();
24059                 break;
24060             case 40: // down
24061                 this.onDecrementMinutes();
24062                 break;
24063             case 13: // enter
24064             case 9: // tab
24065                 this.setTime();
24066                 break;
24067         }
24068     },
24069     
24070     onClick: function(e) {
24071         e.stopPropagation();
24072         e.preventDefault();
24073     },
24074     
24075     picker : function()
24076     {
24077         return this.pickerEl;
24078     },
24079     
24080     fillTime: function()
24081     {    
24082         var time = this.pop.select('tbody', true).first();
24083         
24084         time.dom.innerHTML = '';
24085         
24086         time.createChild({
24087             tag: 'tr',
24088             cn: [
24089                 {
24090                     tag: 'td',
24091                     cn: [
24092                         {
24093                             tag: 'a',
24094                             href: '#',
24095                             cls: 'btn',
24096                             cn: [
24097                                 {
24098                                     tag: 'i',
24099                                     cls: 'hours-up fa fas fa-chevron-up'
24100                                 }
24101                             ]
24102                         } 
24103                     ]
24104                 },
24105                 {
24106                     tag: 'td',
24107                     cls: 'separator'
24108                 },
24109                 {
24110                     tag: 'td',
24111                     cn: [
24112                         {
24113                             tag: 'a',
24114                             href: '#',
24115                             cls: 'btn',
24116                             cn: [
24117                                 {
24118                                     tag: 'i',
24119                                     cls: 'minutes-up fa fas fa-chevron-up'
24120                                 }
24121                             ]
24122                         }
24123                     ]
24124                 },
24125                 {
24126                     tag: 'td',
24127                     cls: 'separator'
24128                 }
24129             ]
24130         });
24131         
24132         time.createChild({
24133             tag: 'tr',
24134             cn: [
24135                 {
24136                     tag: 'td',
24137                     cn: [
24138                         {
24139                             tag: 'span',
24140                             cls: 'timepicker-hour',
24141                             html: '00'
24142                         }  
24143                     ]
24144                 },
24145                 {
24146                     tag: 'td',
24147                     cls: 'separator',
24148                     html: ':'
24149                 },
24150                 {
24151                     tag: 'td',
24152                     cn: [
24153                         {
24154                             tag: 'span',
24155                             cls: 'timepicker-minute',
24156                             html: '00'
24157                         }  
24158                     ]
24159                 },
24160                 {
24161                     tag: 'td',
24162                     cls: 'separator'
24163                 },
24164                 {
24165                     tag: 'td',
24166                     cn: [
24167                         {
24168                             tag: 'button',
24169                             type: 'button',
24170                             cls: 'btn btn-primary period',
24171                             html: 'AM'
24172                             
24173                         }
24174                     ]
24175                 }
24176             ]
24177         });
24178         
24179         time.createChild({
24180             tag: 'tr',
24181             cn: [
24182                 {
24183                     tag: 'td',
24184                     cn: [
24185                         {
24186                             tag: 'a',
24187                             href: '#',
24188                             cls: 'btn',
24189                             cn: [
24190                                 {
24191                                     tag: 'span',
24192                                     cls: 'hours-down fa fas fa-chevron-down'
24193                                 }
24194                             ]
24195                         }
24196                     ]
24197                 },
24198                 {
24199                     tag: 'td',
24200                     cls: 'separator'
24201                 },
24202                 {
24203                     tag: 'td',
24204                     cn: [
24205                         {
24206                             tag: 'a',
24207                             href: '#',
24208                             cls: 'btn',
24209                             cn: [
24210                                 {
24211                                     tag: 'span',
24212                                     cls: 'minutes-down fa fas fa-chevron-down'
24213                                 }
24214                             ]
24215                         }
24216                     ]
24217                 },
24218                 {
24219                     tag: 'td',
24220                     cls: 'separator'
24221                 }
24222             ]
24223         });
24224         
24225     },
24226     
24227     update: function()
24228     {
24229         // default minute is a multiple of minuteStep
24230         if(typeof(this.time) === 'undefined') {
24231             this.time = new Date();
24232             this.time = this.time.add(Date.MINUTE, Math.round(parseInt(this.time.format('i')) / this.minuteStep) * this.minuteStep - parseInt(this.time.format('i')));
24233         }
24234         this.time = (typeof(this.time) === 'undefined') ? new Date() : this.time;
24235         
24236         this.fill();
24237     },
24238     
24239     fill: function() 
24240     {
24241         var hours = this.time.getHours();
24242         var minutes = this.time.getMinutes();
24243         var period = 'AM';
24244         
24245         if(hours > 11){
24246             period = 'PM';
24247         }
24248         
24249         if(hours == 0){
24250             hours = 12;
24251         }
24252         
24253         
24254         if(hours > 12){
24255             hours = hours - 12;
24256         }
24257         
24258         if(hours < 10){
24259             hours = '0' + hours;
24260         }
24261         
24262         if(minutes < 10){
24263             minutes = '0' + minutes;
24264         }
24265         
24266         this.pop.select('.timepicker-hour', true).first().dom.innerHTML = hours;
24267         this.pop.select('.timepicker-minute', true).first().dom.innerHTML = minutes;
24268         this.pop.select('button', true).first().dom.innerHTML = period;
24269         
24270     },
24271     
24272     place: function()
24273     {   
24274         this.picker().removeClass(['bottom-left', 'bottom-right', 'top-left', 'top-right']);
24275         
24276         var cls = ['bottom'];
24277         
24278         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){ // top
24279             cls.pop();
24280             cls.push('top');
24281         }
24282         
24283         cls.push('right');
24284         
24285         if((Roo.lib.Dom.getViewWidth() + Roo.get(document.body).getScroll().left) - (this.inputEl().getLeft() + this.picker().getWidth()) < 0){ // left
24286             cls.pop();
24287             cls.push('left');
24288         }
24289         //this.picker().setXY(20000,20000);
24290         this.picker().addClass(cls.join('-'));
24291         
24292         var _this = this;
24293         
24294         Roo.each(cls, function(c){
24295             if(c == 'bottom'){
24296                 (function() {
24297                  //  
24298                 }).defer(200);
24299                  _this.picker().alignTo(_this.inputEl(),   "tr-br", [0, 10], false);
24300                 //_this.picker().setTop(_this.inputEl().getHeight());
24301                 return;
24302             }
24303             if(c == 'top'){
24304                  _this.picker().alignTo(_this.inputEl(),   "br-tr", [0, 10], false);
24305                 
24306                 //_this.picker().setTop(0 - _this.picker().getHeight());
24307                 return;
24308             }
24309             /*
24310             if(c == 'left'){
24311                 _this.picker().setLeft(_this.inputEl().getLeft() + _this.inputEl().getWidth() - _this.el.getLeft() - _this.picker().getWidth());
24312                 return;
24313             }
24314             if(c == 'right'){
24315                 _this.picker().setLeft(_this.inputEl().getLeft() - _this.el.getLeft());
24316                 return;
24317             }
24318             */
24319         });
24320         
24321     },
24322   
24323     onFocus : function()
24324     {
24325         Roo.bootstrap.form.TimeField.superclass.onFocus.call(this);
24326         this.show();
24327     },
24328     
24329     onBlur : function()
24330     {
24331         Roo.bootstrap.form.TimeField.superclass.onBlur.call(this);
24332         this.hide();
24333     },
24334     
24335     show : function()
24336     {
24337         this.picker().show();
24338         this.pop.show();
24339         this.update();
24340         this.place();
24341         
24342         this.fireEvent('show', this, this.date);
24343     },
24344     
24345     hide : function()
24346     {
24347         this.picker().hide();
24348         this.pop.hide();
24349         
24350         this.fireEvent('hide', this, this.date);
24351     },
24352     
24353     setTime : function()
24354     {
24355         this.hide();
24356         this.setValue(this.time.format(this.format));
24357         
24358         this.fireEvent('select', this, this.date);
24359         
24360         
24361     },
24362     
24363     onMousedown: function(e){
24364         e.stopPropagation();
24365         e.preventDefault();
24366     },
24367     
24368     onIncrementHours: function()
24369     {
24370         Roo.log('onIncrementHours');
24371         this.time = this.time.add(Date.HOUR, 1);
24372         this.update();
24373         
24374     },
24375     
24376     onDecrementHours: function()
24377     {
24378         Roo.log('onDecrementHours');
24379         this.time = this.time.add(Date.HOUR, -1);
24380         this.update();
24381     },
24382     
24383     onIncrementMinutes: function()
24384     {
24385         Roo.log('onIncrementMinutes');
24386         var minutesToAdd = Math.round((parseInt(this.time.format('i')) + this.minuteStep) / this.minuteStep) * this.minuteStep - parseInt(this.time.format('i'));
24387         this.time = this.time.add(Date.MINUTE, minutesToAdd);
24388         this.update();
24389     },
24390     
24391     onDecrementMinutes: function()
24392     {
24393         Roo.log('onDecrementMinutes');
24394         var minutesToSubtract = parseInt(this.time.format('i')) - Math.round((parseInt(this.time.format('i')) - this.minuteStep) / this.minuteStep) * this.minuteStep;
24395         this.time = this.time.add(Date.MINUTE, -1 * minutesToSubtract);
24396         this.update();
24397     },
24398     
24399     onTogglePeriod: function()
24400     {
24401         Roo.log('onTogglePeriod');
24402         this.time = this.time.add(Date.HOUR, 12);
24403         this.update();
24404     }
24405     
24406    
24407 });
24408  
24409
24410 Roo.apply(Roo.bootstrap.form.TimeField,  {
24411   
24412     template : {
24413         tag: 'div',
24414         cls: 'datepicker dropdown-menu',
24415         cn: [
24416             {
24417                 tag: 'div',
24418                 cls: 'datepicker-time',
24419                 cn: [
24420                 {
24421                     tag: 'table',
24422                     cls: 'table-condensed',
24423                     cn:[
24424                         {
24425                             tag: 'tbody',
24426                             cn: [
24427                                 {
24428                                     tag: 'tr',
24429                                     cn: [
24430                                     {
24431                                         tag: 'td',
24432                                         colspan: '7'
24433                                     }
24434                                     ]
24435                                 }
24436                             ]
24437                         },
24438                         {
24439                             tag: 'tfoot',
24440                             cn: [
24441                                 {
24442                                     tag: 'tr',
24443                                     cn: [
24444                                     {
24445                                         tag: 'th',
24446                                         colspan: '7',
24447                                         cls: '',
24448                                         cn: [
24449                                             {
24450                                                 tag: 'button',
24451                                                 cls: 'btn btn-info ok',
24452                                                 html: 'OK'
24453                                             }
24454                                         ]
24455                                     }
24456                     
24457                                     ]
24458                                 }
24459                             ]
24460                         }
24461                     ]
24462                 }
24463                 ]
24464             }
24465         ]
24466     }
24467 });
24468
24469  
24470
24471  /*
24472  * - LGPL
24473  *
24474  * MonthField
24475  * 
24476  */
24477
24478 /**
24479  * @class Roo.bootstrap.form.MonthField
24480  * @extends Roo.bootstrap.form.Input
24481  * Bootstrap MonthField class
24482  * 
24483  * @cfg {String} language default en
24484  * 
24485  * @constructor
24486  * Create a new MonthField
24487  * @param {Object} config The config object
24488  */
24489
24490 Roo.bootstrap.form.MonthField = function(config){
24491     Roo.bootstrap.form.MonthField.superclass.constructor.call(this, config);
24492     
24493     this.addEvents({
24494         /**
24495          * @event show
24496          * Fires when this field show.
24497          * @param {Roo.bootstrap.form.MonthField} this
24498          * @param {Mixed} date The date value
24499          */
24500         show : true,
24501         /**
24502          * @event show
24503          * Fires when this field hide.
24504          * @param {Roo.bootstrap.form.MonthField} this
24505          * @param {Mixed} date The date value
24506          */
24507         hide : true,
24508         /**
24509          * @event select
24510          * Fires when select a date.
24511          * @param {Roo.bootstrap.form.MonthField} this
24512          * @param {String} oldvalue The old value
24513          * @param {String} newvalue The new value
24514          */
24515         select : true
24516     });
24517 };
24518
24519 Roo.extend(Roo.bootstrap.form.MonthField, Roo.bootstrap.form.Input,  {
24520     
24521     onRender: function(ct, position)
24522     {
24523         
24524         Roo.bootstrap.form.MonthField.superclass.onRender.call(this, ct, position);
24525         
24526         this.language = this.language || 'en';
24527         this.language = this.language in Roo.bootstrap.form.MonthField.dates ? this.language : this.language.split('-')[0];
24528         this.language = this.language in Roo.bootstrap.form.MonthField.dates ? this.language : "en";
24529         
24530         this.isRTL = Roo.bootstrap.form.MonthField.dates[this.language].rtl || false;
24531         this.isInline = false;
24532         this.isInput = true;
24533         this.component = this.el.select('.add-on', true).first() || false;
24534         this.component = (this.component && this.component.length === 0) ? false : this.component;
24535         this.hasInput = this.component && this.inputEL().length;
24536         
24537         this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.form.MonthField.template);
24538         
24539         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
24540         
24541         this.picker().on('mousedown', this.onMousedown, this);
24542         this.picker().on('click', this.onClick, this);
24543         
24544         this.picker().addClass('datepicker-dropdown');
24545         
24546         Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
24547             v.setStyle('width', '189px');
24548         });
24549         
24550         this.fillMonths();
24551         
24552         this.update();
24553         
24554         if(this.isInline) {
24555             this.show();
24556         }
24557         
24558     },
24559     
24560     setValue: function(v, suppressEvent)
24561     {   
24562         var o = this.getValue();
24563         
24564         Roo.bootstrap.form.MonthField.superclass.setValue.call(this, v);
24565         
24566         this.update();
24567
24568         if(suppressEvent !== true){
24569             this.fireEvent('select', this, o, v);
24570         }
24571         
24572     },
24573     
24574     getValue: function()
24575     {
24576         return this.value;
24577     },
24578     
24579     onClick: function(e) 
24580     {
24581         e.stopPropagation();
24582         e.preventDefault();
24583         
24584         var target = e.getTarget();
24585         
24586         if(target.nodeName.toLowerCase() === 'i'){
24587             target = Roo.get(target).dom.parentNode;
24588         }
24589         
24590         var nodeName = target.nodeName;
24591         var className = target.className;
24592         var html = target.innerHTML;
24593         
24594         if(nodeName.toLowerCase() != 'span' || className.indexOf('disabled') > -1 || className.indexOf('month') == -1){
24595             return;
24596         }
24597         
24598         this.vIndex = Roo.bootstrap.form.MonthField.dates[this.language].monthsShort.indexOf(html);
24599         
24600         this.setValue(Roo.bootstrap.form.MonthField.dates[this.language].months[this.vIndex]);
24601         
24602         this.hide();
24603                         
24604     },
24605     
24606     picker : function()
24607     {
24608         return this.pickerEl;
24609     },
24610     
24611     fillMonths: function()
24612     {    
24613         var i = 0;
24614         var months = this.picker().select('>.datepicker-months td', true).first();
24615         
24616         months.dom.innerHTML = '';
24617         
24618         while (i < 12) {
24619             var month = {
24620                 tag: 'span',
24621                 cls: 'month',
24622                 html: Roo.bootstrap.form.MonthField.dates[this.language].monthsShort[i++]
24623             };
24624             
24625             months.createChild(month);
24626         }
24627         
24628     },
24629     
24630     update: function()
24631     {
24632         var _this = this;
24633         
24634         if(typeof(this.vIndex) == 'undefined' && this.value.length){
24635             this.vIndex = Roo.bootstrap.form.MonthField.dates[this.language].months.indexOf(this.value);
24636         }
24637         
24638         Roo.each(this.pickerEl.select('> .datepicker-months tbody > tr > td > span', true).elements, function(e, k){
24639             e.removeClass('active');
24640             
24641             if(typeof(_this.vIndex) != 'undefined' && k == _this.vIndex){
24642                 e.addClass('active');
24643             }
24644         })
24645     },
24646     
24647     place: function()
24648     {
24649         if(this.isInline) {
24650             return;
24651         }
24652         
24653         this.picker().removeClass(['bottom', 'top']);
24654         
24655         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
24656             /*
24657              * place to the top of element!
24658              *
24659              */
24660             
24661             this.picker().addClass('top');
24662             this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
24663             
24664             return;
24665         }
24666         
24667         this.picker().addClass('bottom');
24668         
24669         this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
24670     },
24671     
24672     onFocus : function()
24673     {
24674         Roo.bootstrap.form.MonthField.superclass.onFocus.call(this);
24675         this.show();
24676     },
24677     
24678     onBlur : function()
24679     {
24680         Roo.bootstrap.form.MonthField.superclass.onBlur.call(this);
24681         
24682         var d = this.inputEl().getValue();
24683         
24684         this.setValue(d);
24685                 
24686         this.hide();
24687     },
24688     
24689     show : function()
24690     {
24691         this.picker().show();
24692         this.picker().select('>.datepicker-months', true).first().show();
24693         this.update();
24694         this.place();
24695         
24696         this.fireEvent('show', this, this.date);
24697     },
24698     
24699     hide : function()
24700     {
24701         if(this.isInline) {
24702             return;
24703         }
24704         this.picker().hide();
24705         this.fireEvent('hide', this, this.date);
24706         
24707     },
24708     
24709     onMousedown: function(e)
24710     {
24711         e.stopPropagation();
24712         e.preventDefault();
24713     },
24714     
24715     keyup: function(e)
24716     {
24717         Roo.bootstrap.form.MonthField.superclass.keyup.call(this);
24718         this.update();
24719     },
24720
24721     fireKey: function(e)
24722     {
24723         if (!this.picker().isVisible()){
24724             if (e.keyCode == 27)   {// allow escape to hide and re-show picker
24725                 this.show();
24726             }
24727             return;
24728         }
24729         
24730         var dir;
24731         
24732         switch(e.keyCode){
24733             case 27: // escape
24734                 this.hide();
24735                 e.preventDefault();
24736                 break;
24737             case 37: // left
24738             case 39: // right
24739                 dir = e.keyCode == 37 ? -1 : 1;
24740                 
24741                 this.vIndex = this.vIndex + dir;
24742                 
24743                 if(this.vIndex < 0){
24744                     this.vIndex = 0;
24745                 }
24746                 
24747                 if(this.vIndex > 11){
24748                     this.vIndex = 11;
24749                 }
24750                 
24751                 if(isNaN(this.vIndex)){
24752                     this.vIndex = 0;
24753                 }
24754                 
24755                 this.setValue(Roo.bootstrap.form.MonthField.dates[this.language].months[this.vIndex]);
24756                 
24757                 break;
24758             case 38: // up
24759             case 40: // down
24760                 
24761                 dir = e.keyCode == 38 ? -1 : 1;
24762                 
24763                 this.vIndex = this.vIndex + dir * 4;
24764                 
24765                 if(this.vIndex < 0){
24766                     this.vIndex = 0;
24767                 }
24768                 
24769                 if(this.vIndex > 11){
24770                     this.vIndex = 11;
24771                 }
24772                 
24773                 if(isNaN(this.vIndex)){
24774                     this.vIndex = 0;
24775                 }
24776                 
24777                 this.setValue(Roo.bootstrap.form.MonthField.dates[this.language].months[this.vIndex]);
24778                 break;
24779                 
24780             case 13: // enter
24781                 
24782                 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
24783                     this.setValue(Roo.bootstrap.form.MonthField.dates[this.language].months[this.vIndex]);
24784                 }
24785                 
24786                 this.hide();
24787                 e.preventDefault();
24788                 break;
24789             case 9: // tab
24790                 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
24791                     this.setValue(Roo.bootstrap.form.MonthField.dates[this.language].months[this.vIndex]);
24792                 }
24793                 this.hide();
24794                 break;
24795             case 16: // shift
24796             case 17: // ctrl
24797             case 18: // alt
24798                 break;
24799             default :
24800                 this.hide();
24801                 
24802         }
24803     },
24804     
24805     remove: function() 
24806     {
24807         this.picker().remove();
24808     }
24809    
24810 });
24811
24812 Roo.apply(Roo.bootstrap.form.MonthField,  {
24813     
24814     content : {
24815         tag: 'tbody',
24816         cn: [
24817         {
24818             tag: 'tr',
24819             cn: [
24820             {
24821                 tag: 'td',
24822                 colspan: '7'
24823             }
24824             ]
24825         }
24826         ]
24827     },
24828     
24829     dates:{
24830         en: {
24831             months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
24832             monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"]
24833         }
24834     }
24835 });
24836
24837 Roo.apply(Roo.bootstrap.form.MonthField,  {
24838   
24839     template : {
24840         tag: 'div',
24841         cls: 'datepicker dropdown-menu roo-dynamic',
24842         cn: [
24843             {
24844                 tag: 'div',
24845                 cls: 'datepicker-months',
24846                 cn: [
24847                 {
24848                     tag: 'table',
24849                     cls: 'table-condensed',
24850                     cn:[
24851                         Roo.bootstrap.form.DateField.content
24852                     ]
24853                 }
24854                 ]
24855             }
24856         ]
24857     }
24858 });
24859
24860  
24861
24862  
24863  /*
24864  * - LGPL
24865  *
24866  * CheckBox
24867  * 
24868  */
24869
24870 /**
24871  * @class Roo.bootstrap.form.CheckBox
24872  * @extends Roo.bootstrap.form.Input
24873  * Bootstrap CheckBox class
24874  * 
24875  * @cfg {String} valueOff The value that should go into the generated input element's value when unchecked.
24876  * @cfg {String} inputValue The value that should go into the generated input element's value when checked.
24877  * @cfg {String} boxLabel The text that appears beside the checkbox
24878  * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the checkbox
24879  * @cfg {Boolean} checked initnal the element
24880  * @cfg {Boolean} inline inline the element (default false)
24881  * @cfg {String} groupId the checkbox group id // normal just use for checkbox
24882  * @cfg {String} tooltip label tooltip
24883  * 
24884  * @constructor
24885  * Create a new CheckBox
24886  * @param {Object} config The config object
24887  */
24888
24889 Roo.bootstrap.form.CheckBox = function(config){
24890     Roo.bootstrap.form.CheckBox.superclass.constructor.call(this, config);
24891    
24892     this.addEvents({
24893         /**
24894         * @event check
24895         * Fires when the element is checked or unchecked.
24896         * @param {Roo.bootstrap.form.CheckBox} this This input
24897         * @param {Boolean} checked The new checked value
24898         */
24899        check : true,
24900        /**
24901         * @event click
24902         * Fires when the element is click.
24903         * @param {Roo.bootstrap.form.CheckBox} this This input
24904         */
24905        click : true
24906     });
24907     
24908 };
24909
24910 Roo.extend(Roo.bootstrap.form.CheckBox, Roo.bootstrap.form.Input,  {
24911   
24912     inputType: 'checkbox',
24913     inputValue: 1,
24914     valueOff: 0,
24915     boxLabel: false,
24916     checked: false,
24917     weight : false,
24918     inline: false,
24919     tooltip : '',
24920     
24921     // checkbox success does not make any sense really.. 
24922     invalidClass : "",
24923     validClass : "",
24924     
24925     
24926     getAutoCreate : function()
24927     {
24928         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
24929         
24930         var id = Roo.id();
24931         
24932         var cfg = {};
24933         
24934         cfg.cls = 'form-group form-check ' + this.inputType; //input-group
24935         
24936         if(this.inline){
24937             cfg.cls += ' ' + this.inputType + '-inline  form-check-inline';
24938         }
24939         
24940         var input =  {
24941             tag: 'input',
24942             id : id,
24943             type : this.inputType,
24944             value : this.inputValue,
24945             cls : 'roo-' + this.inputType, //'form-box',
24946             placeholder : this.placeholder || ''
24947             
24948         };
24949         
24950         if(this.inputType != 'radio'){
24951             var hidden =  {
24952                 tag: 'input',
24953                 type : 'hidden',
24954                 cls : 'roo-hidden-value',
24955                 value : this.checked ? this.inputValue : this.valueOff
24956             };
24957         }
24958         
24959             
24960         if (this.weight) { // Validity check?
24961             cfg.cls += " " + this.inputType + "-" + this.weight;
24962         }
24963         
24964         if (this.disabled) {
24965             input.disabled=true;
24966         }
24967         
24968         if(this.checked){
24969             input.checked = this.checked;
24970         }
24971         
24972         if (this.name) {
24973             
24974             input.name = this.name;
24975             
24976             if(this.inputType != 'radio'){
24977                 hidden.name = this.name;
24978                 input.name = '_hidden_' + this.name;
24979             }
24980         }
24981         
24982         if (this.size) {
24983             input.cls += ' input-' + this.size;
24984         }
24985         
24986         var settings=this;
24987         
24988         ['xs','sm','md','lg'].map(function(size){
24989             if (settings[size]) {
24990                 cfg.cls += ' col-' + size + '-' + settings[size];
24991             }
24992         });
24993         
24994         var inputblock = input;
24995          
24996         if (this.before || this.after) {
24997             
24998             inputblock = {
24999                 cls : 'input-group',
25000                 cn :  [] 
25001             };
25002             
25003             if (this.before) {
25004                 inputblock.cn.push({
25005                     tag :'span',
25006                     cls : 'input-group-addon',
25007                     html : this.before
25008                 });
25009             }
25010             
25011             inputblock.cn.push(input);
25012             
25013             if(this.inputType != 'radio'){
25014                 inputblock.cn.push(hidden);
25015             }
25016             
25017             if (this.after) {
25018                 inputblock.cn.push({
25019                     tag :'span',
25020                     cls : 'input-group-addon',
25021                     html : this.after
25022                 });
25023             }
25024             
25025         }
25026         var boxLabelCfg = false;
25027         
25028         if(this.boxLabel){
25029            
25030             boxLabelCfg = {
25031                 tag: 'label',
25032                 //'for': id, // box label is handled by onclick - so no for...
25033                 cls: 'box-label',
25034                 html: this.boxLabel
25035             };
25036             if(this.tooltip){
25037                 boxLabelCfg.tooltip = this.tooltip;
25038             }
25039              
25040         }
25041         
25042         
25043         if (align ==='left' && this.fieldLabel.length) {
25044 //                Roo.log("left and has label");
25045             cfg.cn = [
25046                 {
25047                     tag: 'label',
25048                     'for' :  id,
25049                     cls : 'control-label',
25050                     html : this.fieldLabel
25051                 },
25052                 {
25053                     cls : "", 
25054                     cn: [
25055                         inputblock
25056                     ]
25057                 }
25058             ];
25059             
25060             if (boxLabelCfg) {
25061                 cfg.cn[1].cn.push(boxLabelCfg);
25062             }
25063             
25064             if(this.labelWidth > 12){
25065                 cfg.cn[0].style = "width: " + this.labelWidth + 'px';
25066             }
25067             
25068             if(this.labelWidth < 13 && this.labelmd == 0){
25069                 this.labelmd = this.labelWidth;
25070             }
25071             
25072             if(this.labellg > 0){
25073                 cfg.cn[0].cls += ' col-lg-' + this.labellg;
25074                 cfg.cn[1].cls += ' col-lg-' + (12 - this.labellg);
25075             }
25076             
25077             if(this.labelmd > 0){
25078                 cfg.cn[0].cls += ' col-md-' + this.labelmd;
25079                 cfg.cn[1].cls += ' col-md-' + (12 - this.labelmd);
25080             }
25081             
25082             if(this.labelsm > 0){
25083                 cfg.cn[0].cls += ' col-sm-' + this.labelsm;
25084                 cfg.cn[1].cls += ' col-sm-' + (12 - this.labelsm);
25085             }
25086             
25087             if(this.labelxs > 0){
25088                 cfg.cn[0].cls += ' col-xs-' + this.labelxs;
25089                 cfg.cn[1].cls += ' col-xs-' + (12 - this.labelxs);
25090             }
25091             
25092         } else if ( this.fieldLabel.length) {
25093 //                Roo.log(" label");
25094                 cfg.cn = [
25095                    
25096                     {
25097                         tag: this.boxLabel ? 'span' : 'label',
25098                         'for': id,
25099                         cls: 'control-label box-input-label',
25100                         //cls : 'input-group-addon',
25101                         html : this.fieldLabel
25102                     },
25103                     
25104                     inputblock
25105                     
25106                 ];
25107                 if (boxLabelCfg) {
25108                     cfg.cn.push(boxLabelCfg);
25109                 }
25110
25111         } else {
25112             
25113 //                Roo.log(" no label && no align");
25114                 cfg.cn = [  inputblock ] ;
25115                 if (boxLabelCfg) {
25116                     cfg.cn.push(boxLabelCfg);
25117                 }
25118
25119                 
25120         }
25121         
25122        
25123         
25124         if(this.inputType != 'radio'){
25125             cfg.cn.push(hidden);
25126         }
25127         
25128         return cfg;
25129         
25130     },
25131     
25132     /**
25133      * return the real input element.
25134      */
25135     inputEl: function ()
25136     {
25137         return this.el.select('input.roo-' + this.inputType,true).first();
25138     },
25139     hiddenEl: function ()
25140     {
25141         return this.el.select('input.roo-hidden-value',true).first();
25142     },
25143     
25144     labelEl: function()
25145     {
25146         return this.el.select('label.control-label',true).first();
25147     },
25148     /* depricated... */
25149     
25150     label: function()
25151     {
25152         return this.labelEl();
25153     },
25154     
25155     boxLabelEl: function()
25156     {
25157         return this.el.select('label.box-label',true).first();
25158     },
25159     
25160     initEvents : function()
25161     {
25162 //        Roo.bootstrap.form.CheckBox.superclass.initEvents.call(this);
25163         
25164         this.inputEl().on('click', this.onClick,  this);
25165         
25166         if (this.boxLabel) { 
25167             this.el.select('label.box-label',true).first().on('click', this.onClick,  this);
25168         }
25169         
25170         this.startValue = this.getValue();
25171         
25172         if(this.groupId){
25173             Roo.bootstrap.form.CheckBox.register(this);
25174         }
25175     },
25176     
25177     onClick : function(e)
25178     {   
25179         if(this.fireEvent('click', this, e) !== false){
25180             this.setChecked(!this.checked);
25181         }
25182         
25183     },
25184     
25185     setChecked : function(state,suppressEvent)
25186     {
25187         this.startValue = this.getValue();
25188
25189         if(this.inputType == 'radio'){
25190             
25191             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
25192                 e.dom.checked = false;
25193             });
25194             
25195             this.inputEl().dom.checked = true;
25196             
25197             this.inputEl().dom.value = this.inputValue;
25198             
25199             if(suppressEvent !== true){
25200                 this.fireEvent('check', this, true);
25201             }
25202             
25203             this.validate();
25204             
25205             return;
25206         }
25207         
25208         this.checked = state;
25209         
25210         this.inputEl().dom.checked = state;
25211         
25212         
25213         this.hiddenEl().dom.value = state ? this.inputValue : this.valueOff;
25214         
25215         if(suppressEvent !== true){
25216             this.fireEvent('check', this, state);
25217         }
25218         
25219         this.validate();
25220     },
25221     
25222     getValue : function()
25223     {
25224         if(this.inputType == 'radio'){
25225             return this.getGroupValue();
25226         }
25227         
25228         return this.hiddenEl().dom.value;
25229         
25230     },
25231     
25232     getGroupValue : function()
25233     {
25234         if(typeof(this.el.up('form').child('input[name='+this.name+']:checked', true)) == 'undefined'){
25235             return '';
25236         }
25237         
25238         return this.el.up('form').child('input[name='+this.name+']:checked', true).value;
25239     },
25240     
25241     setValue : function(v,suppressEvent)
25242     {
25243         if(this.inputType == 'radio'){
25244             this.setGroupValue(v, suppressEvent);
25245             return;
25246         }
25247         
25248         this.setChecked(((typeof(v) == 'undefined') ? this.checked : (String(v) === String(this.inputValue))), suppressEvent);
25249         
25250         this.validate();
25251     },
25252     
25253     setGroupValue : function(v, suppressEvent)
25254     {
25255         this.startValue = this.getValue();
25256         
25257         Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
25258             e.dom.checked = false;
25259             
25260             if(e.dom.value == v){
25261                 e.dom.checked = true;
25262             }
25263         });
25264         
25265         if(suppressEvent !== true){
25266             this.fireEvent('check', this, true);
25267         }
25268
25269         this.validate();
25270         
25271         return;
25272     },
25273     
25274     validate : function()
25275     {
25276         if(this.getVisibilityEl().hasClass('hidden')){
25277             return true;
25278         }
25279         
25280         if(
25281                 this.disabled || 
25282                 (this.inputType == 'radio' && this.validateRadio()) ||
25283                 (this.inputType == 'checkbox' && this.validateCheckbox())
25284         ){
25285             this.markValid();
25286             return true;
25287         }
25288         
25289         this.markInvalid();
25290         return false;
25291     },
25292     
25293     validateRadio : function()
25294     {
25295         if(this.getVisibilityEl().hasClass('hidden')){
25296             return true;
25297         }
25298         
25299         if(this.allowBlank){
25300             return true;
25301         }
25302         
25303         var valid = false;
25304         
25305         Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
25306             if(!e.dom.checked){
25307                 return;
25308             }
25309             
25310             valid = true;
25311             
25312             return false;
25313         });
25314         
25315         return valid;
25316     },
25317     
25318     validateCheckbox : function()
25319     {
25320         if(!this.groupId){
25321             return (this.getValue() == this.inputValue || this.allowBlank) ? true : false;
25322             //return (this.getValue() == this.inputValue) ? true : false;
25323         }
25324         
25325         var group = Roo.bootstrap.form.CheckBox.get(this.groupId);
25326         
25327         if(!group){
25328             return false;
25329         }
25330         
25331         var r = false;
25332         
25333         for(var i in group){
25334             if(group[i].el.isVisible(true)){
25335                 r = false;
25336                 break;
25337             }
25338             
25339             r = true;
25340         }
25341         
25342         for(var i in group){
25343             if(r){
25344                 break;
25345             }
25346             
25347             r = (group[i].getValue() == group[i].inputValue) ? true : false;
25348         }
25349         
25350         return r;
25351     },
25352     
25353     /**
25354      * Mark this field as valid
25355      */
25356     markValid : function()
25357     {
25358         var _this = this;
25359         
25360         this.fireEvent('valid', this);
25361         
25362         var label = Roo.bootstrap.form.FieldLabel.get(this.name + '-group');
25363         
25364         if(this.groupId){
25365             label = Roo.bootstrap.form.FieldLabel.get(this.groupId + '-group');
25366         }
25367         
25368         if(label){
25369             label.markValid();
25370         }
25371
25372         if(this.inputType == 'radio'){
25373             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
25374                 var fg = e.findParent('.form-group', false, true);
25375                 if (Roo.bootstrap.version == 3) {
25376                     fg.removeClass([_this.invalidClass, _this.validClass]);
25377                     fg.addClass(_this.validClass);
25378                 } else {
25379                     fg.removeClass(['is-valid', 'is-invalid']);
25380                     fg.addClass('is-valid');
25381                 }
25382             });
25383             
25384             return;
25385         }
25386
25387         if(!this.groupId){
25388             var fg = this.el.findParent('.form-group', false, true);
25389             if (Roo.bootstrap.version == 3) {
25390                 fg.removeClass([this.invalidClass, this.validClass]);
25391                 fg.addClass(this.validClass);
25392             } else {
25393                 fg.removeClass(['is-valid', 'is-invalid']);
25394                 fg.addClass('is-valid');
25395             }
25396             return;
25397         }
25398         
25399         var group = Roo.bootstrap.form.CheckBox.get(this.groupId);
25400         
25401         if(!group){
25402             return;
25403         }
25404         
25405         for(var i in group){
25406             var fg = group[i].el.findParent('.form-group', false, true);
25407             if (Roo.bootstrap.version == 3) {
25408                 fg.removeClass([this.invalidClass, this.validClass]);
25409                 fg.addClass(this.validClass);
25410             } else {
25411                 fg.removeClass(['is-valid', 'is-invalid']);
25412                 fg.addClass('is-valid');
25413             }
25414         }
25415     },
25416     
25417      /**
25418      * Mark this field as invalid
25419      * @param {String} msg The validation message
25420      */
25421     markInvalid : function(msg)
25422     {
25423         if(this.allowBlank){
25424             return;
25425         }
25426         
25427         var _this = this;
25428         
25429         this.fireEvent('invalid', this, msg);
25430         
25431         var label = Roo.bootstrap.form.FieldLabel.get(this.name + '-group');
25432         
25433         if(this.groupId){
25434             label = Roo.bootstrap.form.FieldLabel.get(this.groupId + '-group');
25435         }
25436         
25437         if(label){
25438             label.markInvalid();
25439         }
25440             
25441         if(this.inputType == 'radio'){
25442             
25443             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
25444                 var fg = e.findParent('.form-group', false, true);
25445                 if (Roo.bootstrap.version == 3) {
25446                     fg.removeClass([_this.invalidClass, _this.validClass]);
25447                     fg.addClass(_this.invalidClass);
25448                 } else {
25449                     fg.removeClass(['is-invalid', 'is-valid']);
25450                     fg.addClass('is-invalid');
25451                 }
25452             });
25453             
25454             return;
25455         }
25456         
25457         if(!this.groupId){
25458             var fg = this.el.findParent('.form-group', false, true);
25459             if (Roo.bootstrap.version == 3) {
25460                 fg.removeClass([_this.invalidClass, _this.validClass]);
25461                 fg.addClass(_this.invalidClass);
25462             } else {
25463                 fg.removeClass(['is-invalid', 'is-valid']);
25464                 fg.addClass('is-invalid');
25465             }
25466             return;
25467         }
25468         
25469         var group = Roo.bootstrap.form.CheckBox.get(this.groupId);
25470         
25471         if(!group){
25472             return;
25473         }
25474         
25475         for(var i in group){
25476             var fg = group[i].el.findParent('.form-group', false, true);
25477             if (Roo.bootstrap.version == 3) {
25478                 fg.removeClass([_this.invalidClass, _this.validClass]);
25479                 fg.addClass(_this.invalidClass);
25480             } else {
25481                 fg.removeClass(['is-invalid', 'is-valid']);
25482                 fg.addClass('is-invalid');
25483             }
25484         }
25485         
25486     },
25487     
25488     clearInvalid : function()
25489     {
25490         Roo.bootstrap.form.Input.prototype.clearInvalid.call(this);
25491         
25492         // this.el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
25493         
25494         var label = Roo.bootstrap.form.FieldLabel.get(this.name + '-group');
25495         
25496         if (label && label.iconEl) {
25497             label.iconEl.removeClass([ label.validClass, label.invalidClass ]);
25498             label.iconEl.removeClass(['is-invalid', 'is-valid']);
25499         }
25500     },
25501     
25502     disable : function()
25503     {
25504         if(this.inputType != 'radio'){
25505             Roo.bootstrap.form.CheckBox.superclass.disable.call(this);
25506             return;
25507         }
25508         
25509         var _this = this;
25510         
25511         if(this.rendered){
25512             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
25513                 _this.getActionEl().addClass(this.disabledClass);
25514                 e.dom.disabled = true;
25515             });
25516         }
25517         
25518         this.disabled = true;
25519         this.fireEvent("disable", this);
25520         return this;
25521     },
25522
25523     enable : function()
25524     {
25525         if(this.inputType != 'radio'){
25526             Roo.bootstrap.form.CheckBox.superclass.enable.call(this);
25527             return;
25528         }
25529         
25530         var _this = this;
25531         
25532         if(this.rendered){
25533             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
25534                 _this.getActionEl().removeClass(this.disabledClass);
25535                 e.dom.disabled = false;
25536             });
25537         }
25538         
25539         this.disabled = false;
25540         this.fireEvent("enable", this);
25541         return this;
25542     },
25543     
25544     setBoxLabel : function(v)
25545     {
25546         this.boxLabel = v;
25547         
25548         if(this.rendered){
25549             this.el.select('label.box-label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
25550         }
25551     }
25552
25553 });
25554
25555 Roo.apply(Roo.bootstrap.form.CheckBox, {
25556     
25557     groups: {},
25558     
25559      /**
25560     * register a CheckBox Group
25561     * @param {Roo.bootstrap.form.CheckBox} the CheckBox to add
25562     */
25563     register : function(checkbox)
25564     {
25565         if(typeof(this.groups[checkbox.groupId]) == 'undefined'){
25566             this.groups[checkbox.groupId] = {};
25567         }
25568         
25569         if(this.groups[checkbox.groupId].hasOwnProperty(checkbox.name)){
25570             return;
25571         }
25572         
25573         this.groups[checkbox.groupId][checkbox.name] = checkbox;
25574         
25575     },
25576     /**
25577     * fetch a CheckBox Group based on the group ID
25578     * @param {string} the group ID
25579     * @returns {Roo.bootstrap.form.CheckBox} the CheckBox group
25580     */
25581     get: function(groupId) {
25582         if (typeof(this.groups[groupId]) == 'undefined') {
25583             return false;
25584         }
25585         
25586         return this.groups[groupId] ;
25587     }
25588     
25589     
25590 });
25591 /*
25592  * - LGPL
25593  *
25594  * RadioItem
25595  * 
25596  */
25597
25598 /**
25599  * @class Roo.bootstrap.form.Radio
25600  * @extends Roo.bootstrap.Component
25601  * Bootstrap Radio class
25602  * @cfg {String} boxLabel - the label associated
25603  * @cfg {String} value - the value of radio
25604  * 
25605  * @constructor
25606  * Create a new Radio
25607  * @param {Object} config The config object
25608  */
25609 Roo.bootstrap.form.Radio = function(config){
25610     Roo.bootstrap.form.Radio.superclass.constructor.call(this, config);
25611     
25612 };
25613
25614 Roo.extend(Roo.bootstrap.form.Radio, Roo.bootstrap.Component, {
25615     
25616     boxLabel : '',
25617     
25618     value : '',
25619     
25620     getAutoCreate : function()
25621     {
25622         var cfg = {
25623             tag : 'div',
25624             cls : 'form-group radio',
25625             cn : [
25626                 {
25627                     tag : 'label',
25628                     cls : 'box-label',
25629                     html : this.boxLabel
25630                 }
25631             ]
25632         };
25633         
25634         return cfg;
25635     },
25636     
25637     initEvents : function() 
25638     {
25639         this.parent().register(this);
25640         
25641         this.el.on('click', this.onClick, this);
25642         
25643     },
25644     
25645     onClick : function(e)
25646     {
25647         if(this.parent().fireEvent('click', this.parent(), this, e) !== false){
25648             this.setChecked(true);
25649         }
25650     },
25651     
25652     setChecked : function(state, suppressEvent)
25653     {
25654         this.parent().setValue(this.value, suppressEvent);
25655         
25656     },
25657     
25658     setBoxLabel : function(v)
25659     {
25660         this.boxLabel = v;
25661         
25662         if(this.rendered){
25663             this.el.select('label.box-label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
25664         }
25665     }
25666     
25667 });
25668  
25669
25670  /*
25671  * - LGPL
25672  *
25673  * Input
25674  * 
25675  */
25676
25677 /**
25678  * @class Roo.bootstrap.form.SecurePass
25679  * @extends Roo.bootstrap.form.Input
25680  * Bootstrap SecurePass class
25681  *
25682  * 
25683  * @constructor
25684  * Create a new SecurePass
25685  * @param {Object} config The config object
25686  */
25687  
25688 Roo.bootstrap.form.SecurePass = function (config) {
25689     // these go here, so the translation tool can replace them..
25690     this.errors = {
25691         PwdEmpty: "Please type a password, and then retype it to confirm.",
25692         PwdShort: "Your password must be at least 6 characters long. Please type a different password.",
25693         PwdLong: "Your password can't contain more than 16 characters. Please type a different password.",
25694         PwdBadChar: "The password contains characters that aren't allowed. Please type a different password.",
25695         IDInPwd: "Your password can't include the part of your ID. Please type a different password.",
25696         FNInPwd: "Your password can't contain your first name. Please type a different password.",
25697         LNInPwd: "Your password can't contain your last name. Please type a different password.",
25698         TooWeak: "Your password is Too Weak."
25699     },
25700     this.meterLabel = "Password strength:";
25701     this.pwdStrengths = ["Too Weak", "Weak", "Medium", "Strong"];
25702     this.meterClass = [
25703         "roo-password-meter-tooweak", 
25704         "roo-password-meter-weak", 
25705         "roo-password-meter-medium", 
25706         "roo-password-meter-strong", 
25707         "roo-password-meter-grey"
25708     ];
25709     
25710     this.errors = {};
25711     
25712     Roo.bootstrap.form.SecurePass.superclass.constructor.call(this, config);
25713 }
25714
25715 Roo.extend(Roo.bootstrap.form.SecurePass, Roo.bootstrap.form.Input, {
25716     /**
25717      * @cfg {String/Object} errors A Error spec, or true for a default spec (defaults to
25718      * {
25719      *  PwdEmpty: "Please type a password, and then retype it to confirm.",
25720      *  PwdShort: "Your password must be at least 6 characters long. Please type a different password.",
25721      *  PwdLong: "Your password can't contain more than 16 characters. Please type a different password.",
25722      *  PwdBadChar: "The password contains characters that aren't allowed. Please type a different password.",
25723      *  IDInPwd: "Your password can't include the part of your ID. Please type a different password.",
25724      *  FNInPwd: "Your password can't contain your first name. Please type a different password.",
25725      *  LNInPwd: "Your password can't contain your last name. Please type a different password."
25726      * })
25727      */
25728     // private
25729     
25730     meterWidth: 300,
25731     errorMsg :'',    
25732     errors: false,
25733     imageRoot: '/',
25734     /**
25735      * @cfg {String/Object} Label for the strength meter (defaults to
25736      * 'Password strength:')
25737      */
25738     // private
25739     meterLabel: '',
25740     /**
25741      * @cfg {String/Object} pwdStrengths A pwdStrengths spec, or true for a default spec (defaults to
25742      * ['Weak', 'Medium', 'Strong'])
25743      */
25744     // private    
25745     pwdStrengths: false,    
25746     // private
25747     strength: 0,
25748     // private
25749     _lastPwd: null,
25750     // private
25751     kCapitalLetter: 0,
25752     kSmallLetter: 1,
25753     kDigit: 2,
25754     kPunctuation: 3,
25755     
25756     insecure: false,
25757     // private
25758     initEvents: function ()
25759     {
25760         Roo.bootstrap.form.SecurePass.superclass.initEvents.call(this);
25761
25762         if (this.el.is('input[type=password]') && Roo.isSafari) {
25763             this.el.on('keydown', this.SafariOnKeyDown, this);
25764         }
25765
25766         this.el.on('keyup', this.checkStrength, this, {buffer: 50});
25767     },
25768     // private
25769     onRender: function (ct, position)
25770     {
25771         Roo.bootstrap.form.SecurePass.superclass.onRender.call(this, ct, position);
25772         this.wrap = this.el.wrap({cls: 'x-form-field-wrap'});
25773         this.trigger = this.wrap.createChild({tag: 'div', cls: 'StrengthMeter ' + this.triggerClass});
25774
25775         this.trigger.createChild({
25776                    cn: [
25777                     {
25778                     //id: 'PwdMeter',
25779                     tag: 'div',
25780                     cls: 'roo-password-meter-grey col-xs-12',
25781                     style: {
25782                         //width: 0,
25783                         //width: this.meterWidth + 'px'                                                
25784                         }
25785                     },
25786                     {                            
25787                          cls: 'roo-password-meter-text'                          
25788                     }
25789                 ]            
25790         });
25791
25792          
25793         if (this.hideTrigger) {
25794             this.trigger.setDisplayed(false);
25795         }
25796         this.setSize(this.width || '', this.height || '');
25797     },
25798     // private
25799     onDestroy: function ()
25800     {
25801         if (this.trigger) {
25802             this.trigger.removeAllListeners();
25803             this.trigger.remove();
25804         }
25805         if (this.wrap) {
25806             this.wrap.remove();
25807         }
25808         Roo.bootstrap.form.TriggerField.superclass.onDestroy.call(this);
25809     },
25810     // private
25811     checkStrength: function ()
25812     {
25813         var pwd = this.inputEl().getValue();
25814         if (pwd == this._lastPwd) {
25815             return;
25816         }
25817
25818         var strength;
25819         if (this.ClientSideStrongPassword(pwd)) {
25820             strength = 3;
25821         } else if (this.ClientSideMediumPassword(pwd)) {
25822             strength = 2;
25823         } else if (this.ClientSideWeakPassword(pwd)) {
25824             strength = 1;
25825         } else {
25826             strength = 0;
25827         }
25828         
25829         Roo.log('strength1: ' + strength);
25830         
25831         //var pm = this.trigger.child('div/div/div').dom;
25832         var pm = this.trigger.child('div/div');
25833         pm.removeClass(this.meterClass);
25834         pm.addClass(this.meterClass[strength]);
25835                 
25836         
25837         var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;        
25838                 
25839         pt.innerHTML = this.meterLabel + '&nbsp;' + this.pwdStrengths[strength];
25840         
25841         this._lastPwd = pwd;
25842     },
25843     reset: function ()
25844     {
25845         Roo.bootstrap.form.SecurePass.superclass.reset.call(this);
25846         
25847         this._lastPwd = '';
25848         
25849         var pm = this.trigger.child('div/div');
25850         pm.removeClass(this.meterClass);
25851         pm.addClass('roo-password-meter-grey');        
25852         
25853         
25854         var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;        
25855         
25856         pt.innerHTML = '';
25857         this.inputEl().dom.type='password';
25858     },
25859     // private
25860     validateValue: function (value)
25861     {
25862         if (!Roo.bootstrap.form.SecurePass.superclass.validateValue.call(this, value)) {
25863             return false;
25864         }
25865         if (value.length == 0) {
25866             if (this.allowBlank) {
25867                 this.clearInvalid();
25868                 return true;
25869             }
25870
25871             this.markInvalid(this.errors.PwdEmpty);
25872             this.errorMsg = this.errors.PwdEmpty;
25873             return false;
25874         }
25875         
25876         if(this.insecure){
25877             return true;
25878         }
25879         
25880         if (!value.match(/[\x21-\x7e]+/)) {
25881             this.markInvalid(this.errors.PwdBadChar);
25882             this.errorMsg = this.errors.PwdBadChar;
25883             return false;
25884         }
25885         if (value.length < 6) {
25886             this.markInvalid(this.errors.PwdShort);
25887             this.errorMsg = this.errors.PwdShort;
25888             return false;
25889         }
25890         if (value.length > 16) {
25891             this.markInvalid(this.errors.PwdLong);
25892             this.errorMsg = this.errors.PwdLong;
25893             return false;
25894         }
25895         var strength;
25896         if (this.ClientSideStrongPassword(value)) {
25897             strength = 3;
25898         } else if (this.ClientSideMediumPassword(value)) {
25899             strength = 2;
25900         } else if (this.ClientSideWeakPassword(value)) {
25901             strength = 1;
25902         } else {
25903             strength = 0;
25904         }
25905
25906         
25907         if (strength < 2) {
25908             //this.markInvalid(this.errors.TooWeak);
25909             this.errorMsg = this.errors.TooWeak;
25910             //return false;
25911         }
25912         
25913         
25914         console.log('strength2: ' + strength);
25915         
25916         //var pm = this.trigger.child('div/div/div').dom;
25917         
25918         var pm = this.trigger.child('div/div');
25919         pm.removeClass(this.meterClass);
25920         pm.addClass(this.meterClass[strength]);
25921                 
25922         var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;        
25923                 
25924         pt.innerHTML = this.meterLabel + '&nbsp;' + this.pwdStrengths[strength];
25925         
25926         this.errorMsg = ''; 
25927         return true;
25928     },
25929     // private
25930     CharacterSetChecks: function (type)
25931     {
25932         this.type = type;
25933         this.fResult = false;
25934     },
25935     // private
25936     isctype: function (character, type)
25937     {
25938         switch (type) {  
25939             case this.kCapitalLetter:
25940                 if (character >= 'A' && character <= 'Z') {
25941                     return true;
25942                 }
25943                 break;
25944             
25945             case this.kSmallLetter:
25946                 if (character >= 'a' && character <= 'z') {
25947                     return true;
25948                 }
25949                 break;
25950             
25951             case this.kDigit:
25952                 if (character >= '0' && character <= '9') {
25953                     return true;
25954                 }
25955                 break;
25956             
25957             case this.kPunctuation:
25958                 if ('!@#$%^&*()_+-=\'";:[{]}|.>,</?`~'.indexOf(character) >= 0) {
25959                     return true;
25960                 }
25961                 break;
25962             
25963             default:
25964                 return false;
25965         }
25966
25967     },
25968     // private
25969     IsLongEnough: function (pwd, size)
25970     {
25971         return !(pwd == null || isNaN(size) || pwd.length < size);
25972     },
25973     // private
25974     SpansEnoughCharacterSets: function (word, nb)
25975     {
25976         if (!this.IsLongEnough(word, nb))
25977         {
25978             return false;
25979         }
25980
25981         var characterSetChecks = new Array(
25982             new this.CharacterSetChecks(this.kCapitalLetter), new this.CharacterSetChecks(this.kSmallLetter),
25983             new this.CharacterSetChecks(this.kDigit), new this.CharacterSetChecks(this.kPunctuation)
25984         );
25985         
25986         for (var index = 0; index < word.length; ++index) {
25987             for (var nCharSet = 0; nCharSet < characterSetChecks.length; ++nCharSet) {
25988                 if (!characterSetChecks[nCharSet].fResult && this.isctype(word.charAt(index), characterSetChecks[nCharSet].type)) {
25989                     characterSetChecks[nCharSet].fResult = true;
25990                     break;
25991                 }
25992             }
25993         }
25994
25995         var nCharSets = 0;
25996         for (var nCharSet = 0; nCharSet < characterSetChecks.length; ++nCharSet) {
25997             if (characterSetChecks[nCharSet].fResult) {
25998                 ++nCharSets;
25999             }
26000         }
26001
26002         if (nCharSets < nb) {
26003             return false;
26004         }
26005         return true;
26006     },
26007     // private
26008     ClientSideStrongPassword: function (pwd)
26009     {
26010         return this.IsLongEnough(pwd, 8) && this.SpansEnoughCharacterSets(pwd, 3);
26011     },
26012     // private
26013     ClientSideMediumPassword: function (pwd)
26014     {
26015         return this.IsLongEnough(pwd, 7) && this.SpansEnoughCharacterSets(pwd, 2);
26016     },
26017     // private
26018     ClientSideWeakPassword: function (pwd)
26019     {
26020         return this.IsLongEnough(pwd, 6) || !this.IsLongEnough(pwd, 0);
26021     }
26022           
26023 });Roo.rtf = {}; // namespace
26024 Roo.rtf.Hex = function(hex)
26025 {
26026     this.hexstr = hex;
26027 };
26028 Roo.rtf.Paragraph = function(opts)
26029 {
26030     this.content = []; ///??? is that used?
26031 };Roo.rtf.Span = function(opts)
26032 {
26033     this.value = opts.value;
26034 };
26035
26036 Roo.rtf.Group = function(parent)
26037 {
26038     // we dont want to acutally store parent - it will make debug a nightmare..
26039     this.content = [];
26040     this.cn  = [];
26041      
26042        
26043     
26044 };
26045
26046 Roo.rtf.Group.prototype = {
26047     ignorable : false,
26048     content: false,
26049     cn: false,
26050     addContent : function(node) {
26051         // could set styles...
26052         this.content.push(node);
26053     },
26054     addChild : function(cn)
26055     {
26056         this.cn.push(cn);
26057     },
26058     // only for images really...
26059     toDataURL : function()
26060     {
26061         var mimetype = false;
26062         switch(true) {
26063             case this.content.filter(function(a) { return a.value == 'pngblip' } ).length > 0: 
26064                 mimetype = "image/png";
26065                 break;
26066              case this.content.filter(function(a) { return a.value == 'jpegblip' } ).length > 0:
26067                 mimetype = "image/jpeg";
26068                 break;
26069             default :
26070                 return 'about:blank'; // ?? error?
26071         }
26072         
26073         
26074         var hexstring = this.content[this.content.length-1].value;
26075         
26076         return 'data:' + mimetype + ';base64,' + btoa(hexstring.match(/\w{2}/g).map(function(a) {
26077             return String.fromCharCode(parseInt(a, 16));
26078         }).join(""));
26079     }
26080     
26081 };
26082 // this looks like it's normally the {rtf{ .... }}
26083 Roo.rtf.Document = function()
26084 {
26085     // we dont want to acutally store parent - it will make debug a nightmare..
26086     this.rtlch  = [];
26087     this.content = [];
26088     this.cn = [];
26089     
26090 };
26091 Roo.extend(Roo.rtf.Document, Roo.rtf.Group, { 
26092     addChild : function(cn)
26093     {
26094         this.cn.push(cn);
26095         switch(cn.type) {
26096             case 'rtlch': // most content seems to be inside this??
26097             case 'listtext':
26098             case 'shpinst':
26099                 this.rtlch.push(cn);
26100                 return;
26101             default:
26102                 this[cn.type] = cn;
26103         }
26104         
26105     },
26106     
26107     getElementsByType : function(type)
26108     {
26109         var ret =  [];
26110         this._getElementsByType(type, ret, this.cn, 'rtf');
26111         return ret;
26112     },
26113     _getElementsByType : function (type, ret, search_array, path)
26114     {
26115         search_array.forEach(function(n,i) {
26116             if (n.type == type) {
26117                 n.path = path + '/' + n.type + ':' + i;
26118                 ret.push(n);
26119             }
26120             if (n.cn.length > 0) {
26121                 this._getElementsByType(type, ret, n.cn, path + '/' + n.type+':'+i);
26122             }
26123         },this);
26124     }
26125     
26126 });
26127  
26128 Roo.rtf.Ctrl = function(opts)
26129 {
26130     this.value = opts.value;
26131     this.param = opts.param;
26132 };
26133 /**
26134  *
26135  *
26136  * based on this https://github.com/iarna/rtf-parser
26137  * it's really only designed to extract pict from pasted RTF 
26138  *
26139  * usage:
26140  *
26141  *  var images = new Roo.rtf.Parser().parse(a_string).filter(function(g) { return g.type == 'pict'; });
26142  *  
26143  *
26144  */
26145
26146  
26147
26148
26149
26150 Roo.rtf.Parser = function(text) {
26151     //super({objectMode: true})
26152     this.text = '';
26153     this.parserState = this.parseText;
26154     
26155     // these are for interpeter...
26156     this.doc = {};
26157     ///this.parserState = this.parseTop
26158     this.groupStack = [];
26159     this.hexStore = [];
26160     this.doc = false;
26161     
26162     this.groups = []; // where we put the return.
26163     
26164     for (var ii = 0; ii < text.length; ++ii) {
26165         ++this.cpos;
26166         
26167         if (text[ii] === '\n') {
26168             ++this.row;
26169             this.col = 1;
26170         } else {
26171             ++this.col;
26172         }
26173         this.parserState(text[ii]);
26174     }
26175     
26176     
26177     
26178 };
26179 Roo.rtf.Parser.prototype = {
26180     text : '', // string being parsed..
26181     controlWord : '',
26182     controlWordParam :  '',
26183     hexChar : '',
26184     doc : false,
26185     group: false,
26186     groupStack : false,
26187     hexStore : false,
26188     
26189     
26190     cpos : 0, 
26191     row : 1, // reportin?
26192     col : 1, //
26193
26194      
26195     push : function (el)
26196     {
26197         var m = 'cmd'+ el.type;
26198         if (typeof(this[m]) == 'undefined') {
26199             Roo.log('invalid cmd:' + el.type);
26200             return;
26201         }
26202         this[m](el);
26203         //Roo.log(el);
26204     },
26205     flushHexStore : function()
26206     {
26207         if (this.hexStore.length < 1) {
26208             return;
26209         }
26210         var hexstr = this.hexStore.map(
26211             function(cmd) {
26212                 return cmd.value;
26213         }).join('');
26214         
26215         this.group.addContent( new Roo.rtf.Hex( hexstr ));
26216               
26217             
26218         this.hexStore.splice(0)
26219         
26220     },
26221     
26222     cmdgroupstart : function()
26223     {
26224         this.flushHexStore();
26225         if (this.group) {
26226             this.groupStack.push(this.group);
26227         }
26228          // parent..
26229         if (this.doc === false) {
26230             this.group = this.doc = new Roo.rtf.Document();
26231             return;
26232             
26233         }
26234         this.group = new Roo.rtf.Group(this.group);
26235     },
26236     cmdignorable : function()
26237     {
26238         this.flushHexStore();
26239         this.group.ignorable = true;
26240     },
26241     cmdendparagraph : function()
26242     {
26243         this.flushHexStore();
26244         this.group.addContent(new Roo.rtf.Paragraph());
26245     },
26246     cmdgroupend : function ()
26247     {
26248         this.flushHexStore();
26249         var endingGroup = this.group;
26250         
26251         
26252         this.group = this.groupStack.pop();
26253         if (this.group) {
26254             this.group.addChild(endingGroup);
26255         }
26256         
26257         
26258         
26259         var doc = this.group || this.doc;
26260         //if (endingGroup instanceof FontTable) {
26261         //  doc.fonts = endingGroup.table
26262         //} else if (endingGroup instanceof ColorTable) {
26263         //  doc.colors = endingGroup.table
26264         //} else if (endingGroup !== this.doc && !endingGroup.get('ignorable')) {
26265         if (endingGroup.ignorable === false) {
26266             //code
26267             this.groups.push(endingGroup);
26268            // Roo.log( endingGroup );
26269         }
26270             //Roo.each(endingGroup.content, function(item)) {
26271             //    doc.addContent(item);
26272             //}
26273             //process.emit('debug', 'GROUP END', endingGroup.type, endingGroup.get('ignorable'))
26274         //}
26275     },
26276     cmdtext : function (cmd)
26277     {
26278         this.flushHexStore();
26279         if (!this.group) { // an RTF fragment, missing the {\rtf1 header
26280             //this.group = this.doc
26281             return;  // we really don't care about stray text...
26282         }
26283         this.group.addContent(new Roo.rtf.Span(cmd));
26284     },
26285     cmdcontrolword : function (cmd)
26286     {
26287         this.flushHexStore();
26288         if (!this.group.type) {
26289             this.group.type = cmd.value;
26290             return;
26291         }
26292         this.group.addContent(new Roo.rtf.Ctrl(cmd));
26293         // we actually don't care about ctrl words...
26294         return ;
26295         /*
26296         var method = 'ctrl$' + cmd.value.replace(/-(.)/g, (_, char) => char.toUpperCase())
26297         if (this[method]) {
26298             this[method](cmd.param)
26299         } else {
26300             if (!this.group.get('ignorable')) process.emit('debug', method, cmd.param)
26301         }
26302         */
26303     },
26304     cmdhexchar : function(cmd) {
26305         this.hexStore.push(cmd);
26306     },
26307     cmderror : function(cmd) {
26308         throw cmd.value;
26309     },
26310     
26311     /*
26312       _flush (done) {
26313         if (this.text !== '\u0000') this.emitText()
26314         done()
26315       }
26316       */
26317       
26318       
26319     parseText : function(c)
26320     {
26321         if (c === '\\') {
26322             this.parserState = this.parseEscapes;
26323         } else if (c === '{') {
26324             this.emitStartGroup();
26325         } else if (c === '}') {
26326             this.emitEndGroup();
26327         } else if (c === '\x0A' || c === '\x0D') {
26328             // cr/lf are noise chars
26329         } else {
26330             this.text += c;
26331         }
26332     },
26333     
26334     parseEscapes: function (c)
26335     {
26336         if (c === '\\' || c === '{' || c === '}') {
26337             this.text += c;
26338             this.parserState = this.parseText;
26339         } else {
26340             this.parserState = this.parseControlSymbol;
26341             this.parseControlSymbol(c);
26342         }
26343     },
26344     parseControlSymbol: function(c)
26345     {
26346         if (c === '~') {
26347             this.text += '\u00a0'; // nbsp
26348             this.parserState = this.parseText
26349         } else if (c === '-') {
26350              this.text += '\u00ad'; // soft hyphen
26351         } else if (c === '_') {
26352             this.text += '\u2011'; // non-breaking hyphen
26353         } else if (c === '*') {
26354             this.emitIgnorable();
26355             this.parserState = this.parseText;
26356         } else if (c === "'") {
26357             this.parserState = this.parseHexChar;
26358         } else if (c === '|') { // formula cacter
26359             this.emitFormula();
26360             this.parserState = this.parseText;
26361         } else if (c === ':') { // subentry in an index entry
26362             this.emitIndexSubEntry();
26363             this.parserState = this.parseText;
26364         } else if (c === '\x0a') {
26365             this.emitEndParagraph();
26366             this.parserState = this.parseText;
26367         } else if (c === '\x0d') {
26368             this.emitEndParagraph();
26369             this.parserState = this.parseText;
26370         } else {
26371             this.parserState = this.parseControlWord;
26372             this.parseControlWord(c);
26373         }
26374     },
26375     parseHexChar: function (c)
26376     {
26377         if (/^[A-Fa-f0-9]$/.test(c)) {
26378             this.hexChar += c;
26379             if (this.hexChar.length >= 2) {
26380               this.emitHexChar();
26381               this.parserState = this.parseText;
26382             }
26383             return;
26384         }
26385         this.emitError("Invalid character \"" + c + "\" in hex literal.");
26386         this.parserState = this.parseText;
26387         
26388     },
26389     parseControlWord : function(c)
26390     {
26391         if (c === ' ') {
26392             this.emitControlWord();
26393             this.parserState = this.parseText;
26394         } else if (/^[-\d]$/.test(c)) {
26395             this.parserState = this.parseControlWordParam;
26396             this.controlWordParam += c;
26397         } else if (/^[A-Za-z]$/.test(c)) {
26398           this.controlWord += c;
26399         } else {
26400           this.emitControlWord();
26401           this.parserState = this.parseText;
26402           this.parseText(c);
26403         }
26404     },
26405     parseControlWordParam : function (c) {
26406         if (/^\d$/.test(c)) {
26407           this.controlWordParam += c;
26408         } else if (c === ' ') {
26409           this.emitControlWord();
26410           this.parserState = this.parseText;
26411         } else {
26412           this.emitControlWord();
26413           this.parserState = this.parseText;
26414           this.parseText(c);
26415         }
26416     },
26417     
26418     
26419     
26420     
26421     emitText : function () {
26422         if (this.text === '') {
26423             return;
26424         }
26425         this.push({
26426             type: 'text',
26427             value: this.text,
26428             pos: this.cpos,
26429             row: this.row,
26430             col: this.col
26431         });
26432         this.text = ''
26433     },
26434     emitControlWord : function ()
26435     {
26436         this.emitText();
26437         if (this.controlWord === '') {
26438             // do we want to track this - it seems just to cause problems.
26439             //this.emitError('empty control word');
26440         } else {
26441             this.push({
26442                   type: 'controlword',
26443                   value: this.controlWord,
26444                   param: this.controlWordParam !== '' && Number(this.controlWordParam),
26445                   pos: this.cpos,
26446                   row: this.row,
26447                   col: this.col
26448             });
26449         }
26450         this.controlWord = '';
26451         this.controlWordParam = '';
26452     },
26453     emitStartGroup : function ()
26454     {
26455         this.emitText();
26456         this.push({
26457             type: 'groupstart',
26458             pos: this.cpos,
26459             row: this.row,
26460             col: this.col
26461         });
26462     },
26463     emitEndGroup : function ()
26464     {
26465         this.emitText();
26466         this.push({
26467             type: 'groupend',
26468             pos: this.cpos,
26469             row: this.row,
26470             col: this.col
26471         });
26472     },
26473     emitIgnorable : function ()
26474     {
26475         this.emitText();
26476         this.push({
26477             type: 'ignorable',
26478             pos: this.cpos,
26479             row: this.row,
26480             col: this.col
26481         });
26482     },
26483     emitHexChar : function ()
26484     {
26485         this.emitText();
26486         this.push({
26487             type: 'hexchar',
26488             value: this.hexChar,
26489             pos: this.cpos,
26490             row: this.row,
26491             col: this.col
26492         });
26493         this.hexChar = ''
26494     },
26495     emitError : function (message)
26496     {
26497       this.emitText();
26498       this.push({
26499             type: 'error',
26500             value: message,
26501             row: this.row,
26502             col: this.col,
26503             char: this.cpos //,
26504             //stack: new Error().stack
26505         });
26506     },
26507     emitEndParagraph : function () {
26508         this.emitText();
26509         this.push({
26510             type: 'endparagraph',
26511             pos: this.cpos,
26512             row: this.row,
26513             col: this.col
26514         });
26515     }
26516      
26517 } ; 
26518 /**
26519  * @class Roo.htmleditor.Filter
26520  * Base Class for filtering htmleditor stuff. - do not use this directly - extend it.
26521  * @cfg {DomElement} node The node to iterate and filter
26522  * @cfg {boolean|String|Array} tag Tags to replace 
26523  * @constructor
26524  * Create a new Filter.
26525  * @param {Object} config Configuration options
26526  */
26527
26528
26529
26530 Roo.htmleditor.Filter = function(cfg) {
26531     Roo.apply(this.cfg);
26532     // this does not actually call walk as it's really just a abstract class
26533 }
26534
26535
26536 Roo.htmleditor.Filter.prototype = {
26537     
26538     node: false,
26539     
26540     tag: false,
26541
26542     // overrride to do replace comments.
26543     replaceComment : false,
26544     
26545     // overrride to do replace or do stuff with tags..
26546     replaceTag : false,
26547     
26548     walk : function(dom)
26549     {
26550         Roo.each( Array.from(dom.childNodes), function( e ) {
26551             switch(true) {
26552                 
26553                 case e.nodeType == 8 &&  this.replaceComment  !== false: // comment
26554                     this.replaceComment(e);
26555                     return;
26556                 
26557                 case e.nodeType != 1: //not a node.
26558                     return;
26559                 
26560                 case this.tag === true: // everything
26561                 case e.tagName.indexOf(":") > -1 && typeof(this.tag) == 'object' && this.tag.indexOf(":") > -1:
26562                 case e.tagName.indexOf(":") > -1 && typeof(this.tag) == 'string' && this.tag == ":":
26563                 case typeof(this.tag) == 'object' && this.tag.indexOf(e.tagName) > -1: // array and it matches.
26564                 case typeof(this.tag) == 'string' && this.tag == e.tagName: // array and it matches.
26565                     if (this.replaceTag && false === this.replaceTag(e)) {
26566                         return;
26567                     }
26568                     if (e.hasChildNodes()) {
26569                         this.walk(e);
26570                     }
26571                     return;
26572                 
26573                 default:    // tags .. that do not match.
26574                     if (e.hasChildNodes()) {
26575                         this.walk(e);
26576                     }
26577             }
26578             
26579         }, this);
26580         
26581     },
26582     
26583     
26584     removeNodeKeepChildren : function( node)
26585     {
26586     
26587         ar = Array.from(node.childNodes);
26588         for (var i = 0; i < ar.length; i++) {
26589          
26590             node.removeChild(ar[i]);
26591             // what if we need to walk these???
26592             node.parentNode.insertBefore(ar[i], node);
26593            
26594         }
26595         node.parentNode.removeChild(node);
26596     }
26597 }; 
26598
26599 /**
26600  * @class Roo.htmleditor.FilterAttributes
26601  * clean attributes and  styles including http:// etc.. in attribute
26602  * @constructor
26603 * Run a new Attribute Filter
26604 * @param {Object} config Configuration options
26605  */
26606 Roo.htmleditor.FilterAttributes = function(cfg)
26607 {
26608     Roo.apply(this, cfg);
26609     this.attrib_black = this.attrib_black || [];
26610     this.attrib_white = this.attrib_white || [];
26611
26612     this.attrib_clean = this.attrib_clean || [];
26613     this.style_white = this.style_white || [];
26614     this.style_black = this.style_black || [];
26615     this.walk(cfg.node);
26616 }
26617
26618 Roo.extend(Roo.htmleditor.FilterAttributes, Roo.htmleditor.Filter,
26619 {
26620     tag: true, // all tags
26621     
26622     attrib_black : false, // array
26623     attrib_clean : false,
26624     attrib_white : false,
26625
26626     style_white : false,
26627     style_black : false,
26628      
26629      
26630     replaceTag : function(node)
26631     {
26632         if (!node.attributes || !node.attributes.length) {
26633             return true;
26634         }
26635         
26636         for (var i = node.attributes.length-1; i > -1 ; i--) {
26637             var a = node.attributes[i];
26638             //console.log(a);
26639             if (this.attrib_white.length && this.attrib_white.indexOf(a.name.toLowerCase()) < 0) {
26640                 node.removeAttribute(a.name);
26641                 continue;
26642             }
26643             
26644             
26645             
26646             if (a.name.toLowerCase().substr(0,2)=='on')  {
26647                 node.removeAttribute(a.name);
26648                 continue;
26649             }
26650             
26651             
26652             if (this.attrib_black.indexOf(a.name.toLowerCase()) > -1) {
26653                 node.removeAttribute(a.name);
26654                 continue;
26655             }
26656             if (this.attrib_clean.indexOf(a.name.toLowerCase()) > -1) {
26657                 this.cleanAttr(node,a.name,a.value); // fixme..
26658                 continue;
26659             }
26660             if (a.name == 'style') {
26661                 this.cleanStyle(node,a.name,a.value);
26662                 continue;
26663             }
26664             /// clean up MS crap..
26665             // tecnically this should be a list of valid class'es..
26666             
26667             
26668             if (a.name == 'class') {
26669                 if (a.value.match(/^Mso/)) {
26670                     node.removeAttribute('class');
26671                 }
26672                 
26673                 if (a.value.match(/^body$/)) {
26674                     node.removeAttribute('class');
26675                 }
26676                 continue;
26677             }
26678             
26679             
26680             // style cleanup!?
26681             // class cleanup?
26682             
26683         }
26684         return true; // clean children
26685     },
26686         
26687     cleanAttr: function(node, n,v)
26688     {
26689         
26690         if (v.match(/^\./) || v.match(/^\//)) {
26691             return;
26692         }
26693         if (v.match(/^(http|https):\/\//)
26694             || v.match(/^mailto:/) 
26695             || v.match(/^ftp:/)
26696             || v.match(/^data:/)
26697             ) {
26698             return;
26699         }
26700         if (v.match(/^#/)) {
26701             return;
26702         }
26703         if (v.match(/^\{/)) { // allow template editing.
26704             return;
26705         }
26706 //            Roo.log("(REMOVE TAG)"+ node.tagName +'.' + n + '=' + v);
26707         node.removeAttribute(n);
26708         
26709     },
26710     cleanStyle : function(node,  n,v)
26711     {
26712         if (v.match(/expression/)) { //XSS?? should we even bother..
26713             node.removeAttribute(n);
26714             return;
26715         }
26716         
26717         var parts = v.split(/;/);
26718         var clean = [];
26719         
26720         Roo.each(parts, function(p) {
26721             p = p.replace(/^\s+/g,'').replace(/\s+$/g,'');
26722             if (!p.length) {
26723                 return true;
26724             }
26725             var l = p.split(':').shift().replace(/\s+/g,'');
26726             l = l.replace(/^\s+/g,'').replace(/\s+$/g,'');
26727             
26728             if ( this.style_black.length && (this.style_black.indexOf(l) > -1 || this.style_black.indexOf(l.toLowerCase()) > -1)) {
26729                 return true;
26730             }
26731             //Roo.log()
26732             // only allow 'c whitelisted system attributes'
26733             if ( this.style_white.length &&  style_white.indexOf(l) < 0 && style_white.indexOf(l.toLowerCase()) < 0 ) {
26734                 return true;
26735             }
26736             
26737             
26738             clean.push(p);
26739             return true;
26740         },this);
26741         if (clean.length) { 
26742             node.setAttribute(n, clean.join(';'));
26743         } else {
26744             node.removeAttribute(n);
26745         }
26746         
26747     }
26748         
26749         
26750         
26751     
26752 });/**
26753  * @class Roo.htmleditor.FilterBlack
26754  * remove blacklisted elements.
26755  * @constructor
26756  * Run a new Blacklisted Filter
26757  * @param {Object} config Configuration options
26758  */
26759
26760 Roo.htmleditor.FilterBlack = function(cfg)
26761 {
26762     Roo.apply(this, cfg);
26763     this.walk(cfg.node);
26764 }
26765
26766 Roo.extend(Roo.htmleditor.FilterBlack, Roo.htmleditor.Filter,
26767 {
26768     tag : true, // all elements.
26769    
26770     replaceTag : function(n)
26771     {
26772         n.parentNode.removeChild(n);
26773     }
26774 });
26775 /**
26776  * @class Roo.htmleditor.FilterComment
26777  * remove comments.
26778  * @constructor
26779 * Run a new Comments Filter
26780 * @param {Object} config Configuration options
26781  */
26782 Roo.htmleditor.FilterComment = function(cfg)
26783 {
26784     this.walk(cfg.node);
26785 }
26786
26787 Roo.extend(Roo.htmleditor.FilterComment, Roo.htmleditor.Filter,
26788 {
26789   
26790     replaceComment : function(n)
26791     {
26792         n.parentNode.removeChild(n);
26793     }
26794 });/**
26795  * @class Roo.htmleditor.FilterKeepChildren
26796  * remove tags but keep children
26797  * @constructor
26798  * Run a new Keep Children Filter
26799  * @param {Object} config Configuration options
26800  */
26801
26802 Roo.htmleditor.FilterKeepChildren = function(cfg)
26803 {
26804     Roo.apply(this, cfg);
26805     if (this.tag === false) {
26806         return; // dont walk.. (you can use this to use this just to do a child removal on a single tag )
26807     }
26808     // hacky?
26809     if ((typeof(this.tag) == 'object' && this.tag.indexOf(":") > -1)) {
26810         this.cleanNamespace = true;
26811     }
26812         
26813     this.walk(cfg.node);
26814 }
26815
26816 Roo.extend(Roo.htmleditor.FilterKeepChildren, Roo.htmleditor.FilterBlack,
26817 {
26818     cleanNamespace : false, // should really be an option, rather than using ':' inside of this tag.
26819   
26820     replaceTag : function(node)
26821     {
26822         // walk children...
26823         //Roo.log(node.tagName);
26824         var ar = Array.from(node.childNodes);
26825         //remove first..
26826         
26827         for (var i = 0; i < ar.length; i++) {
26828             var e = ar[i];
26829             if (e.nodeType == 1) {
26830                 if (
26831                     (typeof(this.tag) == 'object' && this.tag.indexOf(e.tagName) > -1)
26832                     || // array and it matches
26833                     (typeof(this.tag) == 'string' && this.tag == e.tagName)
26834                     ||
26835                     (e.tagName.indexOf(":") > -1 && typeof(this.tag) == 'object' && this.tag.indexOf(":") > -1)
26836                     ||
26837                     (e.tagName.indexOf(":") > -1 && typeof(this.tag) == 'string' && this.tag == ":")
26838                 ) {
26839                     this.replaceTag(ar[i]); // child is blacklisted as well...
26840                     continue;
26841                 }
26842             }
26843         }  
26844         ar = Array.from(node.childNodes);
26845         for (var i = 0; i < ar.length; i++) {
26846          
26847             node.removeChild(ar[i]);
26848             // what if we need to walk these???
26849             node.parentNode.insertBefore(ar[i], node);
26850             if (this.tag !== false) {
26851                 this.walk(ar[i]);
26852                 
26853             }
26854         }
26855         //Roo.log("REMOVE:" + node.tagName);
26856         node.parentNode.removeChild(node);
26857         return false; // don't walk children
26858         
26859         
26860     }
26861 });/**
26862  * @class Roo.htmleditor.FilterParagraph
26863  * paragraphs cause a nightmare for shared content - this filter is designed to be called ? at various points when editing
26864  * like on 'push' to remove the <p> tags and replace them with line breaks.
26865  * @constructor
26866  * Run a new Paragraph Filter
26867  * @param {Object} config Configuration options
26868  */
26869
26870 Roo.htmleditor.FilterParagraph = function(cfg)
26871 {
26872     // no need to apply config.
26873     this.walk(cfg.node);
26874 }
26875
26876 Roo.extend(Roo.htmleditor.FilterParagraph, Roo.htmleditor.Filter,
26877 {
26878     
26879      
26880     tag : 'P',
26881     
26882      
26883     replaceTag : function(node)
26884     {
26885         
26886         if (node.childNodes.length == 1 &&
26887             node.childNodes[0].nodeType == 3 &&
26888             node.childNodes[0].textContent.trim().length < 1
26889             ) {
26890             // remove and replace with '<BR>';
26891             node.parentNode.replaceChild(node.ownerDocument.createElement('BR'),node);
26892             return false; // no need to walk..
26893         }
26894         var ar = Array.from(node.childNodes);
26895         for (var i = 0; i < ar.length; i++) {
26896             node.removeChild(ar[i]);
26897             // what if we need to walk these???
26898             node.parentNode.insertBefore(ar[i], node);
26899         }
26900         // now what about this?
26901         // <p> &nbsp; </p>
26902         
26903         // double BR.
26904         node.parentNode.insertBefore(node.ownerDocument.createElement('BR'), node);
26905         node.parentNode.insertBefore(node.ownerDocument.createElement('BR'), node);
26906         node.parentNode.removeChild(node);
26907         
26908         return false;
26909
26910     }
26911     
26912 });/**
26913  * @class Roo.htmleditor.FilterSpan
26914  * filter span's with no attributes out..
26915  * @constructor
26916  * Run a new Span Filter
26917  * @param {Object} config Configuration options
26918  */
26919
26920 Roo.htmleditor.FilterSpan = function(cfg)
26921 {
26922     // no need to apply config.
26923     this.walk(cfg.node);
26924 }
26925
26926 Roo.extend(Roo.htmleditor.FilterSpan, Roo.htmleditor.FilterKeepChildren,
26927 {
26928      
26929     tag : 'SPAN',
26930      
26931  
26932     replaceTag : function(node)
26933     {
26934         if (node.attributes && node.attributes.length > 0) {
26935             return true; // walk if there are any.
26936         }
26937         Roo.htmleditor.FilterKeepChildren.prototype.replaceTag.call(this, node);
26938         return false;
26939      
26940     }
26941     
26942 });/**
26943  * @class Roo.htmleditor.FilterTableWidth
26944   try and remove table width data - as that frequently messes up other stuff.
26945  * 
26946  *      was cleanTableWidths.
26947  *
26948  * Quite often pasting from word etc.. results in tables with column and widths.
26949  * This does not work well on fluid HTML layouts - like emails. - so this code should hunt an destroy them..
26950  *
26951  * @constructor
26952  * Run a new Table Filter
26953  * @param {Object} config Configuration options
26954  */
26955
26956 Roo.htmleditor.FilterTableWidth = function(cfg)
26957 {
26958     // no need to apply config.
26959     this.tag = ['TABLE', 'TD', 'TR', 'TH', 'THEAD', 'TBODY' ];
26960     this.walk(cfg.node);
26961 }
26962
26963 Roo.extend(Roo.htmleditor.FilterTableWidth, Roo.htmleditor.Filter,
26964 {
26965      
26966      
26967     
26968     replaceTag: function(node) {
26969         
26970         
26971       
26972         if (node.hasAttribute('width')) {
26973             node.removeAttribute('width');
26974         }
26975         
26976          
26977         if (node.hasAttribute("style")) {
26978             // pretty basic...
26979             
26980             var styles = node.getAttribute("style").split(";");
26981             var nstyle = [];
26982             Roo.each(styles, function(s) {
26983                 if (!s.match(/:/)) {
26984                     return;
26985                 }
26986                 var kv = s.split(":");
26987                 if (kv[0].match(/^\s*(width|min-width)\s*$/)) {
26988                     return;
26989                 }
26990                 // what ever is left... we allow.
26991                 nstyle.push(s);
26992             });
26993             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
26994             if (!nstyle.length) {
26995                 node.removeAttribute('style');
26996             }
26997         }
26998         
26999         return true; // continue doing children..
27000     }
27001 });/**
27002  * @class Roo.htmleditor.FilterWord
27003  * try and clean up all the mess that Word generates.
27004  * 
27005  * This is the 'nice version' - see 'Heavy' that white lists a very short list of elements, and multi-filters 
27006  
27007  * @constructor
27008  * Run a new Span Filter
27009  * @param {Object} config Configuration options
27010  */
27011
27012 Roo.htmleditor.FilterWord = function(cfg)
27013 {
27014     // no need to apply config.
27015     this.replaceDocBullets(cfg.node);
27016     
27017     this.replaceAname(cfg.node);
27018     // this is disabled as the removal is done by other filters;
27019    // this.walk(cfg.node);
27020     this.replaceImageTable(cfg.node);
27021     
27022 }
27023
27024 Roo.extend(Roo.htmleditor.FilterWord, Roo.htmleditor.Filter,
27025 {
27026     tag: true,
27027      
27028     
27029     /**
27030      * Clean up MS wordisms...
27031      */
27032     replaceTag : function(node)
27033     {
27034          
27035         // no idea what this does - span with text, replaceds with just text.
27036         if(
27037                 node.nodeName == 'SPAN' &&
27038                 !node.hasAttributes() &&
27039                 node.childNodes.length == 1 &&
27040                 node.firstChild.nodeName == "#text"  
27041         ) {
27042             var textNode = node.firstChild;
27043             node.removeChild(textNode);
27044             if (node.getAttribute('lang') != 'zh-CN') {   // do not space pad on chinese characters..
27045                 node.parentNode.insertBefore(node.ownerDocument.createTextNode(" "), node);
27046             }
27047             node.parentNode.insertBefore(textNode, node);
27048             if (node.getAttribute('lang') != 'zh-CN') {   // do not space pad on chinese characters..
27049                 node.parentNode.insertBefore(node.ownerDocument.createTextNode(" ") , node);
27050             }
27051             
27052             node.parentNode.removeChild(node);
27053             return false; // dont do chidren - we have remove our node - so no need to do chdhilren?
27054         }
27055         
27056    
27057         
27058         if (node.tagName.toLowerCase().match(/^(style|script|applet|embed|noframes|noscript)$/)) {
27059             node.parentNode.removeChild(node);
27060             return false; // dont do chidlren
27061         }
27062         //Roo.log(node.tagName);
27063         // remove - but keep children..
27064         if (node.tagName.toLowerCase().match(/^(meta|link|\\?xml:|st1:|o:|v:|font)/)) {
27065             //Roo.log('-- removed');
27066             while (node.childNodes.length) {
27067                 var cn = node.childNodes[0];
27068                 node.removeChild(cn);
27069                 node.parentNode.insertBefore(cn, node);
27070                 // move node to parent - and clean it..
27071                 if (cn.nodeType == 1) {
27072                     this.replaceTag(cn);
27073                 }
27074                 
27075             }
27076             node.parentNode.removeChild(node);
27077             /// no need to iterate chidlren = it's got none..
27078             //this.iterateChildren(node, this.cleanWord);
27079             return false; // no need to iterate children.
27080         }
27081         // clean styles
27082         if (node.className.length) {
27083             
27084             var cn = node.className.split(/\W+/);
27085             var cna = [];
27086             Roo.each(cn, function(cls) {
27087                 if (cls.match(/Mso[a-zA-Z]+/)) {
27088                     return;
27089                 }
27090                 cna.push(cls);
27091             });
27092             node.className = cna.length ? cna.join(' ') : '';
27093             if (!cna.length) {
27094                 node.removeAttribute("class");
27095             }
27096         }
27097         
27098         if (node.hasAttribute("lang")) {
27099             node.removeAttribute("lang");
27100         }
27101         
27102         if (node.hasAttribute("style")) {
27103             
27104             var styles = node.getAttribute("style").split(";");
27105             var nstyle = [];
27106             Roo.each(styles, function(s) {
27107                 if (!s.match(/:/)) {
27108                     return;
27109                 }
27110                 var kv = s.split(":");
27111                 if (kv[0].match(/^(mso-|line|font|background|margin|padding|color)/)) {
27112                     return;
27113                 }
27114                 // what ever is left... we allow.
27115                 nstyle.push(s);
27116             });
27117             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
27118             if (!nstyle.length) {
27119                 node.removeAttribute('style');
27120             }
27121         }
27122         return true; // do children
27123         
27124         
27125         
27126     },
27127     
27128     styleToObject: function(node)
27129     {
27130         var styles = (node.getAttribute("style") || '').split(";");
27131         var ret = {};
27132         Roo.each(styles, function(s) {
27133             if (!s.match(/:/)) {
27134                 return;
27135             }
27136             var kv = s.split(":");
27137              
27138             // what ever is left... we allow.
27139             ret[kv[0].trim()] = kv[1];
27140         });
27141         return ret;
27142     },
27143     
27144     
27145     replaceAname : function (doc)
27146     {
27147         // replace all the a/name without..
27148         var aa = Array.from(doc.getElementsByTagName('a'));
27149         for (var i = 0; i  < aa.length; i++) {
27150             var a = aa[i];
27151             if (a.hasAttribute("name")) {
27152                 a.removeAttribute("name");
27153             }
27154             if (a.hasAttribute("href")) {
27155                 continue;
27156             }
27157             // reparent children.
27158             this.removeNodeKeepChildren(a);
27159             
27160         }
27161         
27162         
27163         
27164     },
27165
27166     
27167     
27168     replaceDocBullets : function(doc)
27169     {
27170         // this is a bit odd - but it appears some indents use ql-indent-1
27171          //Roo.log(doc.innerHTML);
27172         
27173         var listpara = Array.from(doc.getElementsByClassName('MsoListParagraphCxSpFirst'));
27174         for( var i = 0; i < listpara.length; i ++) {
27175             listpara[i].className = "MsoListParagraph";
27176         }
27177         
27178         listpara =  Array.from(doc.getElementsByClassName('MsoListParagraphCxSpMiddle'));
27179         for( var i = 0; i < listpara.length; i ++) {
27180             listpara[i].className = "MsoListParagraph";
27181         }
27182         listpara =  Array.from(doc.getElementsByClassName('MsoListParagraphCxSpLast'));
27183         for( var i = 0; i < listpara.length; i ++) {
27184             listpara[i].className = "MsoListParagraph";
27185         }
27186         listpara =  Array.from(doc.getElementsByClassName('ql-indent-1'));
27187         for( var i = 0; i < listpara.length; i ++) {
27188             listpara[i].className = "MsoListParagraph";
27189         }
27190         
27191         // this is a bit hacky - we had one word document where h2 had a miso-list attribute.
27192         var htwo =  Array.from(doc.getElementsByTagName('h2'));
27193         for( var i = 0; i < htwo.length; i ++) {
27194             if (htwo[i].hasAttribute('style') && htwo[i].getAttribute('style').match(/mso-list:/)) {
27195                 htwo[i].className = "MsoListParagraph";
27196             }
27197         }
27198         listpara =  Array.from(doc.getElementsByClassName('MsoNormal'));
27199         for( var i = 0; i < listpara.length; i ++) {
27200             if (listpara[i].hasAttribute('style') && listpara[i].getAttribute('style').match(/mso-list:/)) {
27201                 listpara[i].className = "MsoListParagraph";
27202             } else {
27203                 listpara[i].className = "MsoNormalx";
27204             }
27205         }
27206        
27207         listpara = doc.getElementsByClassName('MsoListParagraph');
27208         // Roo.log(doc.innerHTML);
27209         
27210         
27211         
27212         while(listpara.length) {
27213             
27214             this.replaceDocBullet(listpara.item(0));
27215         }
27216       
27217     },
27218     
27219      
27220     
27221     replaceDocBullet : function(p)
27222     {
27223         // gather all the siblings.
27224         var ns = p,
27225             parent = p.parentNode,
27226             doc = parent.ownerDocument,
27227             items = [];
27228          
27229         //Roo.log("Parsing: " + p.innerText)    ;
27230         var listtype = 'ul';   
27231         while (ns) {
27232             if (ns.nodeType != 1) {
27233                 ns = ns.nextSibling;
27234                 continue;
27235             }
27236             if (!ns.className.match(/(MsoListParagraph|ql-indent-1)/i)) {
27237                 //Roo.log("Missing para r q1indent - got:" + ns.className);
27238                 break;
27239             }
27240             var spans = ns.getElementsByTagName('span');
27241             
27242             if (ns.hasAttribute('style') && ns.getAttribute('style').match(/mso-list/)) {
27243                 items.push(ns);
27244                 ns = ns.nextSibling;
27245                 has_list = true;
27246                 if (!spans.length) {
27247                     continue;
27248                 }
27249                 var ff = '';
27250                 var se = spans[0];
27251                 for (var i = 0; i < spans.length;i++) {
27252                     se = spans[i];
27253                     if (se.hasAttribute('style')  && se.hasAttribute('style') && se.style.fontFamily != '') {
27254                         ff = se.style.fontFamily;
27255                         break;
27256                     }
27257                 }
27258                  
27259                     
27260                 //Roo.log("got font family: " + ff);
27261                 if (typeof(ff) != 'undefined' && !ff.match(/(Symbol|Wingdings)/) && "·o".indexOf(se.innerText.trim()) < 0) {
27262                     listtype = 'ol';
27263                 }
27264                 
27265                 continue;
27266             }
27267             //Roo.log("no mso-list?");
27268             
27269             var spans = ns.getElementsByTagName('span');
27270             if (!spans.length) {
27271                 break;
27272             }
27273             var has_list  = false;
27274             for(var i = 0; i < spans.length; i++) {
27275                 if (spans[i].hasAttribute('style') && spans[i].getAttribute('style').match(/mso-list/)) {
27276                     has_list = true;
27277                     break;
27278                 }
27279             }
27280             if (!has_list) {
27281                 break;
27282             }
27283             items.push(ns);
27284             ns = ns.nextSibling;
27285             
27286             
27287         }
27288         if (!items.length) {
27289             ns.className = "";
27290             return;
27291         }
27292         
27293         var ul = parent.ownerDocument.createElement(listtype); // what about number lists...
27294         parent.insertBefore(ul, p);
27295         var lvl = 0;
27296         var stack = [ ul ];
27297         var last_li = false;
27298         
27299         var margin_to_depth = {};
27300         max_margins = -1;
27301         
27302         items.forEach(function(n, ipos) {
27303             //Roo.log("got innertHMLT=" + n.innerHTML);
27304             
27305             var spans = n.getElementsByTagName('span');
27306             if (!spans.length) {
27307                 //Roo.log("No spans found");
27308                  
27309                 parent.removeChild(n);
27310                 
27311                 
27312                 return; // skip it...
27313             }
27314            
27315                 
27316             var num = 1;
27317             var style = {};
27318             for(var i = 0; i < spans.length; i++) {
27319             
27320                 style = this.styleToObject(spans[i]);
27321                 if (typeof(style['mso-list']) == 'undefined') {
27322                     continue;
27323                 }
27324                 if (listtype == 'ol') {
27325                    num = spans[i].innerText.replace(/[^0-9]+]/g,'')  * 1;
27326                 }
27327                 spans[i].parentNode.removeChild(spans[i]); // remove the fake bullet.
27328                 break;
27329             }
27330             //Roo.log("NOW GOT innertHMLT=" + n.innerHTML);
27331             style = this.styleToObject(n); // mo-list is from the parent node.
27332             if (typeof(style['mso-list']) == 'undefined') {
27333                 //Roo.log("parent is missing level");
27334                   
27335                 parent.removeChild(n);
27336                  
27337                 return;
27338             }
27339             
27340             var margin = style['margin-left'];
27341             if (typeof(margin_to_depth[margin]) == 'undefined') {
27342                 max_margins++;
27343                 margin_to_depth[margin] = max_margins;
27344             }
27345             nlvl = margin_to_depth[margin] ;
27346              
27347             if (nlvl > lvl) {
27348                 //new indent
27349                 var nul = doc.createElement(listtype); // what about number lists...
27350                 if (!last_li) {
27351                     last_li = doc.createElement('li');
27352                     stack[lvl].appendChild(last_li);
27353                 }
27354                 last_li.appendChild(nul);
27355                 stack[nlvl] = nul;
27356                 
27357             }
27358             lvl = nlvl;
27359             
27360             // not starting at 1..
27361             if (!stack[nlvl].hasAttribute("start") && listtype == "ol") {
27362                 stack[nlvl].setAttribute("start", num);
27363             }
27364             
27365             var nli = stack[nlvl].appendChild(doc.createElement('li'));
27366             last_li = nli;
27367             nli.innerHTML = n.innerHTML;
27368             //Roo.log("innerHTML = " + n.innerHTML);
27369             parent.removeChild(n);
27370             
27371              
27372              
27373             
27374         },this);
27375         
27376         
27377         
27378         
27379     },
27380     
27381     replaceImageTable : function(doc)
27382     {
27383          /*
27384           <table cellpadding=0 cellspacing=0 align=left>
27385   <tr>
27386    <td width=423 height=0></td>
27387   </tr>
27388   <tr>
27389    <td></td>
27390    <td><img width=601 height=401
27391    src="file:///C:/Users/Alan/AppData/Local/Temp/msohtmlclip1/01/clip_image002.jpg"
27392    v:shapes="Picture_x0020_2"></td>
27393   </tr>
27394  </table>
27395  */
27396         var imgs = Array.from(doc.getElementsByTagName('img'));
27397         Roo.each(imgs, function(img) {
27398             var td = img.parentNode;
27399             if (td.nodeName !=  'TD') {
27400                 return;
27401             }
27402             var tr = td.parentNode;
27403             if (tr.nodeName !=  'TR') {
27404                 return;
27405             }
27406             var tbody = tr.parentNode;
27407             if (tbody.nodeName !=  'TBODY') {
27408                 return;
27409             }
27410             var table = tbody.parentNode;
27411             if (table.nodeName !=  'TABLE') {
27412                 return;
27413             }
27414             // first row..
27415             
27416             if (table.getElementsByTagName('tr').length != 2) {
27417                 return;
27418             }
27419             if (table.getElementsByTagName('td').length != 3) {
27420                 return;
27421             }
27422             if (table.innerText.trim() != '') {
27423                 return;
27424             }
27425             var p = table.parentNode;
27426             img.parentNode.removeChild(img);
27427             p.insertBefore(img, table);
27428             p.removeChild(table);
27429             
27430             
27431             
27432         });
27433         
27434       
27435     }
27436     
27437 });
27438 /**
27439  * @class Roo.htmleditor.FilterStyleToTag
27440  * part of the word stuff... - certain 'styles' should be converted to tags.
27441  * eg.
27442  *   font-weight: bold -> bold
27443  *   ?? super / subscrit etc..
27444  * 
27445  * @constructor
27446 * Run a new style to tag filter.
27447 * @param {Object} config Configuration options
27448  */
27449 Roo.htmleditor.FilterStyleToTag = function(cfg)
27450 {
27451     
27452     this.tags = {
27453         B  : [ 'fontWeight' , 'bold'],
27454         I :  [ 'fontStyle' , 'italic'],
27455         //pre :  [ 'font-style' , 'italic'],
27456         // h1.. h6 ?? font-size?
27457         SUP : [ 'verticalAlign' , 'super' ],
27458         SUB : [ 'verticalAlign' , 'sub' ]
27459         
27460         
27461     };
27462     
27463     Roo.apply(this, cfg);
27464      
27465     
27466     this.walk(cfg.node);
27467     
27468     
27469     
27470 }
27471
27472
27473 Roo.extend(Roo.htmleditor.FilterStyleToTag, Roo.htmleditor.Filter,
27474 {
27475     tag: true, // all tags
27476     
27477     tags : false,
27478     
27479     
27480     replaceTag : function(node)
27481     {
27482         
27483         
27484         if (node.getAttribute("style") === null) {
27485             return true;
27486         }
27487         var inject = [];
27488         for (var k in this.tags) {
27489             if (node.style[this.tags[k][0]] == this.tags[k][1]) {
27490                 inject.push(k);
27491                 node.style.removeProperty(this.tags[k][0]);
27492             }
27493         }
27494         if (!inject.length) {
27495             return true; 
27496         }
27497         var cn = Array.from(node.childNodes);
27498         var nn = node;
27499         Roo.each(inject, function(t) {
27500             var nc = node.ownerDocument.createElement(t);
27501             nn.appendChild(nc);
27502             nn = nc;
27503         });
27504         for(var i = 0;i < cn.length;cn++) {
27505             node.removeChild(cn[i]);
27506             nn.appendChild(cn[i]);
27507         }
27508         return true /// iterate thru
27509     }
27510     
27511 })/**
27512  * @class Roo.htmleditor.FilterLongBr
27513  * BR/BR/BR - keep a maximum of 2...
27514  * @constructor
27515  * Run a new Long BR Filter
27516  * @param {Object} config Configuration options
27517  */
27518
27519 Roo.htmleditor.FilterLongBr = function(cfg)
27520 {
27521     // no need to apply config.
27522     this.walk(cfg.node);
27523 }
27524
27525 Roo.extend(Roo.htmleditor.FilterLongBr, Roo.htmleditor.Filter,
27526 {
27527     
27528      
27529     tag : 'BR',
27530     
27531      
27532     replaceTag : function(node)
27533     {
27534         
27535         var ps = node.nextSibling;
27536         while (ps && ps.nodeType == 3 && ps.nodeValue.trim().length < 1) {
27537             ps = ps.nextSibling;
27538         }
27539         
27540         if (!ps &&  [ 'TD', 'TH', 'LI', 'H1', 'H2', 'H3', 'H4', 'H5', 'H6' ].indexOf(node.parentNode.tagName) > -1) { 
27541             node.parentNode.removeChild(node); // remove last BR inside one fo these tags
27542             return false;
27543         }
27544         
27545         if (!ps || ps.nodeType != 1) {
27546             return false;
27547         }
27548         
27549         if (!ps || ps.tagName != 'BR') {
27550            
27551             return false;
27552         }
27553         
27554         
27555         
27556         
27557         
27558         if (!node.previousSibling) {
27559             return false;
27560         }
27561         var ps = node.previousSibling;
27562         
27563         while (ps && ps.nodeType == 3 && ps.nodeValue.trim().length < 1) {
27564             ps = ps.previousSibling;
27565         }
27566         if (!ps || ps.nodeType != 1) {
27567             return false;
27568         }
27569         // if header or BR before.. then it's a candidate for removal.. - as we only want '2' of these..
27570         if (!ps || [ 'BR', 'H1', 'H2', 'H3', 'H4', 'H5', 'H6' ].indexOf(ps.tagName) < 0) {
27571             return false;
27572         }
27573         
27574         node.parentNode.removeChild(node); // remove me...
27575         
27576         return false; // no need to do children
27577
27578     }
27579     
27580 }); 
27581
27582 /**
27583  * @class Roo.htmleditor.FilterBlock
27584  * removes id / data-block and contenteditable that are associated with blocks
27585  * usage should be done on a cloned copy of the dom
27586  * @constructor
27587 * Run a new Attribute Filter { node : xxxx }}
27588 * @param {Object} config Configuration options
27589  */
27590 Roo.htmleditor.FilterBlock = function(cfg)
27591 {
27592     Roo.apply(this, cfg);
27593     var qa = cfg.node.querySelectorAll;
27594     this.removeAttributes('data-block');
27595     this.removeAttributes('contenteditable');
27596     this.removeAttributes('id');
27597     
27598 }
27599
27600 Roo.apply(Roo.htmleditor.FilterBlock.prototype,
27601 {
27602     node: true, // all tags
27603      
27604      
27605     removeAttributes : function(attr)
27606     {
27607         var ar = this.node.querySelectorAll('*[' + attr + ']');
27608         for (var i =0;i<ar.length;i++) {
27609             ar[i].removeAttribute(attr);
27610         }
27611     }
27612         
27613         
27614         
27615     
27616 });
27617 /***
27618  * This is based loosely on tinymce 
27619  * @class Roo.htmleditor.TidySerializer
27620  * https://github.com/thorn0/tinymce.html/blob/master/tinymce.html.js
27621  * @constructor
27622  * @method Serializer
27623  * @param {Object} settings Name/value settings object.
27624  */
27625
27626
27627 Roo.htmleditor.TidySerializer = function(settings)
27628 {
27629     Roo.apply(this, settings);
27630     
27631     this.writer = new Roo.htmleditor.TidyWriter(settings);
27632     
27633     
27634
27635 };
27636 Roo.htmleditor.TidySerializer.prototype = {
27637     
27638     /**
27639      * @param {boolean} inner do the inner of the node.
27640      */
27641     inner : false,
27642     
27643     writer : false,
27644     
27645     /**
27646     * Serializes the specified node into a string.
27647     *
27648     * @example
27649     * new tinymce.html.Serializer().serialize(new tinymce.html.DomParser().parse('<p>text</p>'));
27650     * @method serialize
27651     * @param {DomElement} node Node instance to serialize.
27652     * @return {String} String with HTML based on DOM tree.
27653     */
27654     serialize : function(node) {
27655         
27656         // = settings.validate;
27657         var writer = this.writer;
27658         var self  = this;
27659         this.handlers = {
27660             // #text
27661             3: function(node) {
27662                 
27663                 writer.text(node.nodeValue, node);
27664             },
27665             // #comment
27666             8: function(node) {
27667                 writer.comment(node.nodeValue);
27668             },
27669             // Processing instruction
27670             7: function(node) {
27671                 writer.pi(node.name, node.nodeValue);
27672             },
27673             // Doctype
27674             10: function(node) {
27675                 writer.doctype(node.nodeValue);
27676             },
27677             // CDATA
27678             4: function(node) {
27679                 writer.cdata(node.nodeValue);
27680             },
27681             // Document fragment
27682             11: function(node) {
27683                 node = node.firstChild;
27684                 if (!node) {
27685                     return;
27686                 }
27687                 while(node) {
27688                     self.walk(node);
27689                     node = node.nextSibling
27690                 }
27691             }
27692         };
27693         writer.reset();
27694         1 != node.nodeType || this.inner ? this.handlers[11](node) : this.walk(node);
27695         return writer.getContent();
27696     },
27697
27698     walk: function(node)
27699     {
27700         var attrName, attrValue, sortedAttrs, i, l, elementRule,
27701             handler = this.handlers[node.nodeType];
27702             
27703         if (handler) {
27704             handler(node);
27705             return;
27706         }
27707     
27708         var name = node.nodeName;
27709         var isEmpty = node.childNodes.length < 1;
27710       
27711         var writer = this.writer;
27712         var attrs = node.attributes;
27713         // Sort attributes
27714         
27715         writer.start(node.nodeName, attrs, isEmpty, node);
27716         if (isEmpty) {
27717             return;
27718         }
27719         node = node.firstChild;
27720         if (!node) {
27721             writer.end(name);
27722             return;
27723         }
27724         while (node) {
27725             this.walk(node);
27726             node = node.nextSibling;
27727         }
27728         writer.end(name);
27729         
27730     
27731     }
27732     // Serialize element and treat all non elements as fragments
27733    
27734 }; 
27735
27736 /***
27737  * This is based loosely on tinymce 
27738  * @class Roo.htmleditor.TidyWriter
27739  * https://github.com/thorn0/tinymce.html/blob/master/tinymce.html.js
27740  *
27741  * Known issues?
27742  * - not tested much with 'PRE' formated elements.
27743  * 
27744  *
27745  *
27746  */
27747
27748 Roo.htmleditor.TidyWriter = function(settings)
27749 {
27750     
27751     // indent, indentBefore, indentAfter, encode, htmlOutput, html = [];
27752     Roo.apply(this, settings);
27753     this.html = [];
27754     this.state = [];
27755      
27756     this.encode = Roo.htmleditor.TidyEntities.getEncodeFunc(settings.entity_encoding || 'raw', settings.entities);
27757   
27758 }
27759 Roo.htmleditor.TidyWriter.prototype = {
27760
27761  
27762     state : false,
27763     
27764     indent :  '  ',
27765     
27766     // part of state...
27767     indentstr : '',
27768     in_pre: false,
27769     in_inline : false,
27770     last_inline : false,
27771     encode : false,
27772      
27773     
27774             /**
27775     * Writes the a start element such as <p id="a">.
27776     *
27777     * @method start
27778     * @param {String} name Name of the element.
27779     * @param {Array} attrs Optional attribute array or undefined if it hasn't any.
27780     * @param {Boolean} empty Optional empty state if the tag should end like <br />.
27781     */
27782     start: function(name, attrs, empty, node)
27783     {
27784         var i, l, attr, value;
27785         
27786         // there are some situations where adding line break && indentation will not work. will not work.
27787         // <span / b / i ... formating?
27788         
27789         var in_inline = this.in_inline || Roo.htmleditor.TidyWriter.inline_elements.indexOf(name) > -1;
27790         var in_pre    = this.in_pre    || Roo.htmleditor.TidyWriter.whitespace_elements.indexOf(name) > -1;
27791         
27792         var is_short   = empty ? Roo.htmleditor.TidyWriter.shortend_elements.indexOf(name) > -1 : false;
27793         
27794         var add_lb = name == 'BR' ? false : in_inline;
27795         
27796         if (!add_lb && !this.in_pre && this.lastElementEndsWS()) {
27797             i_inline = false;
27798         }
27799
27800         var indentstr =  this.indentstr;
27801         
27802         // e_inline = elements that can be inline, but still allow \n before and after?
27803         // only 'BR' ??? any others?
27804         
27805         // ADD LINE BEFORE tage
27806         if (!this.in_pre) {
27807             if (in_inline) {
27808                 //code
27809                 if (name == 'BR') {
27810                     this.addLine();
27811                 } else if (this.lastElementEndsWS()) {
27812                     this.addLine();
27813                 } else{
27814                     // otherwise - no new line. (and dont indent.)
27815                     indentstr = '';
27816                 }
27817                 
27818             } else {
27819                 this.addLine();
27820             }
27821         } else {
27822             indentstr = '';
27823         }
27824         
27825         this.html.push(indentstr + '<', name.toLowerCase());
27826         
27827         if (attrs) {
27828             for (i = 0, l = attrs.length; i < l; i++) {
27829                 attr = attrs[i];
27830                 this.html.push(' ', attr.name, '="', this.encode(attr.value, true), '"');
27831             }
27832         }
27833      
27834         if (empty) {
27835             if (is_short) {
27836                 this.html[this.html.length] = '/>';
27837             } else {
27838                 this.html[this.html.length] = '></' + name.toLowerCase() + '>';
27839             }
27840             var e_inline = name == 'BR' ? false : this.in_inline;
27841             
27842             if (!e_inline && !this.in_pre) {
27843                 this.addLine();
27844             }
27845             return;
27846         
27847         }
27848         // not empty..
27849         this.html[this.html.length] = '>';
27850         
27851         // there is a special situation, where we need to turn on in_inline - if any of the imediate chidlren are one of these.
27852         /*
27853         if (!in_inline && !in_pre) {
27854             var cn = node.firstChild;
27855             while(cn) {
27856                 if (Roo.htmleditor.TidyWriter.inline_elements.indexOf(cn.nodeName) > -1) {
27857                     in_inline = true
27858                     break;
27859                 }
27860                 cn = cn.nextSibling;
27861             }
27862              
27863         }
27864         */
27865         
27866         
27867         this.pushState({
27868             indentstr : in_pre   ? '' : (this.indentstr + this.indent),
27869             in_pre : in_pre,
27870             in_inline :  in_inline
27871         });
27872         // add a line after if we are not in a
27873         
27874         if (!in_inline && !in_pre) {
27875             this.addLine();
27876         }
27877         
27878             
27879          
27880         
27881     },
27882     
27883     lastElementEndsWS : function()
27884     {
27885         var value = this.html.length > 0 ? this.html[this.html.length-1] : false;
27886         if (value === false) {
27887             return true;
27888         }
27889         return value.match(/\s+$/);
27890         
27891     },
27892     
27893     /**
27894      * Writes the a end element such as </p>.
27895      *
27896      * @method end
27897      * @param {String} name Name of the element.
27898      */
27899     end: function(name) {
27900         var value;
27901         this.popState();
27902         var indentstr = '';
27903         var in_inline = this.in_inline || Roo.htmleditor.TidyWriter.inline_elements.indexOf(name) > -1;
27904         
27905         if (!this.in_pre && !in_inline) {
27906             this.addLine();
27907             indentstr  = this.indentstr;
27908         }
27909         this.html.push(indentstr + '</', name.toLowerCase(), '>');
27910         this.last_inline = in_inline;
27911         
27912         // pop the indent state..
27913     },
27914     /**
27915      * Writes a text node.
27916      *
27917      * In pre - we should not mess with the contents.
27918      * 
27919      *
27920      * @method text
27921      * @param {String} text String to write out.
27922      * @param {Boolean} raw Optional raw state if true the contents wont get encoded.
27923      */
27924     text: function(in_text, node)
27925     {
27926         // if not in whitespace critical
27927         if (in_text.length < 1) {
27928             return;
27929         }
27930         var text = new XMLSerializer().serializeToString(document.createTextNode(in_text)); // escape it properly?
27931         
27932         if (this.in_pre) {
27933             this.html[this.html.length] =  text;
27934             return;   
27935         }
27936         
27937         if (this.in_inline) {
27938             text = text.replace(/\s+/g,' '); // all white space inc line breaks to a slingle' '
27939             if (text != ' ') {
27940                 text = text.replace(/\s+/,' ');  // all white space to single white space
27941                 
27942                     
27943                 // if next tag is '<BR>', then we can trim right..
27944                 if (node.nextSibling &&
27945                     node.nextSibling.nodeType == 1 &&
27946                     node.nextSibling.nodeName == 'BR' )
27947                 {
27948                     text = text.replace(/\s+$/g,'');
27949                 }
27950                 // if previous tag was a BR, we can also trim..
27951                 if (node.previousSibling &&
27952                     node.previousSibling.nodeType == 1 &&
27953                     node.previousSibling.nodeName == 'BR' )
27954                 {
27955                     text = this.indentstr +  text.replace(/^\s+/g,'');
27956                 }
27957                 if (text.match(/\n/)) {
27958                     text = text.replace(
27959                         /(?![^\n]{1,64}$)([^\n]{1,64})\s/g, '$1\n' + this.indentstr
27960                     );
27961                     // remoeve the last whitespace / line break.
27962                     text = text.replace(/\n\s+$/,'');
27963                 }
27964                 // repace long lines
27965                 
27966             }
27967              
27968             this.html[this.html.length] =  text;
27969             return;   
27970         }
27971         // see if previous element was a inline element.
27972         var indentstr = this.indentstr;
27973    
27974         text = text.replace(/\s+/g," "); // all whitespace into single white space.
27975         
27976         // should trim left?
27977         if (node.previousSibling &&
27978             node.previousSibling.nodeType == 1 &&
27979             Roo.htmleditor.TidyWriter.inline_elements.indexOf(node.previousSibling.nodeName) > -1)
27980         {
27981             indentstr = '';
27982             
27983         } else {
27984             this.addLine();
27985             text = text.replace(/^\s+/,''); // trim left
27986           
27987         }
27988         // should trim right?
27989         if (node.nextSibling &&
27990             node.nextSibling.nodeType == 1 &&
27991             Roo.htmleditor.TidyWriter.inline_elements.indexOf(node.nextSibling.nodeName) > -1)
27992         {
27993           // noop
27994             
27995         }  else {
27996             text = text.replace(/\s+$/,''); // trim right
27997         }
27998          
27999               
28000         
28001         
28002         
28003         if (text.length < 1) {
28004             return;
28005         }
28006         if (!text.match(/\n/)) {
28007             this.html.push(indentstr + text);
28008             return;
28009         }
28010         
28011         text = this.indentstr + text.replace(
28012             /(?![^\n]{1,64}$)([^\n]{1,64})\s/g, '$1\n' + this.indentstr
28013         );
28014         // remoeve the last whitespace / line break.
28015         text = text.replace(/\s+$/,''); 
28016         
28017         this.html.push(text);
28018         
28019         // split and indent..
28020         
28021         
28022     },
28023     /**
28024      * Writes a cdata node such as <![CDATA[data]]>.
28025      *
28026      * @method cdata
28027      * @param {String} text String to write out inside the cdata.
28028      */
28029     cdata: function(text) {
28030         this.html.push('<![CDATA[', text, ']]>');
28031     },
28032     /**
28033     * Writes a comment node such as <!-- Comment -->.
28034     *
28035     * @method cdata
28036     * @param {String} text String to write out inside the comment.
28037     */
28038    comment: function(text) {
28039        this.html.push('<!--', text, '-->');
28040    },
28041     /**
28042      * Writes a PI node such as <?xml attr="value" ?>.
28043      *
28044      * @method pi
28045      * @param {String} name Name of the pi.
28046      * @param {String} text String to write out inside the pi.
28047      */
28048     pi: function(name, text) {
28049         text ? this.html.push('<?', name, ' ', this.encode(text), '?>') : this.html.push('<?', name, '?>');
28050         this.indent != '' && this.html.push('\n');
28051     },
28052     /**
28053      * Writes a doctype node such as <!DOCTYPE data>.
28054      *
28055      * @method doctype
28056      * @param {String} text String to write out inside the doctype.
28057      */
28058     doctype: function(text) {
28059         this.html.push('<!DOCTYPE', text, '>', this.indent != '' ? '\n' : '');
28060     },
28061     /**
28062      * Resets the internal buffer if one wants to reuse the writer.
28063      *
28064      * @method reset
28065      */
28066     reset: function() {
28067         this.html.length = 0;
28068         this.state = [];
28069         this.pushState({
28070             indentstr : '',
28071             in_pre : false, 
28072             in_inline : false
28073         })
28074     },
28075     /**
28076      * Returns the contents that got serialized.
28077      *
28078      * @method getContent
28079      * @return {String} HTML contents that got written down.
28080      */
28081     getContent: function() {
28082         return this.html.join('').replace(/\n$/, '');
28083     },
28084     
28085     pushState : function(cfg)
28086     {
28087         this.state.push(cfg);
28088         Roo.apply(this, cfg);
28089     },
28090     
28091     popState : function()
28092     {
28093         if (this.state.length < 1) {
28094             return; // nothing to push
28095         }
28096         var cfg = {
28097             in_pre: false,
28098             indentstr : ''
28099         };
28100         this.state.pop();
28101         if (this.state.length > 0) {
28102             cfg = this.state[this.state.length-1]; 
28103         }
28104         Roo.apply(this, cfg);
28105     },
28106     
28107     addLine: function()
28108     {
28109         if (this.html.length < 1) {
28110             return;
28111         }
28112         
28113         
28114         var value = this.html[this.html.length - 1];
28115         if (value.length > 0 && '\n' !== value) {
28116             this.html.push('\n');
28117         }
28118     }
28119     
28120     
28121 //'pre script noscript style textarea video audio iframe object code'
28122 // shortended... 'area base basefont br col frame hr img input isindex link  meta param embed source wbr track');
28123 // inline 
28124 };
28125
28126 Roo.htmleditor.TidyWriter.inline_elements = [
28127         'SPAN','STRONG','B','EM','I','FONT','STRIKE','U','VAR',
28128         'CITE','DFN','CODE','MARK','Q','SUP','SUB','SAMP', 'A'
28129 ];
28130 Roo.htmleditor.TidyWriter.shortend_elements = [
28131     'AREA','BASE','BASEFONT','BR','COL','FRAME','HR','IMG','INPUT',
28132     'ISINDEX','LINK','','META','PARAM','EMBED','SOURCE','WBR','TRACK'
28133 ];
28134
28135 Roo.htmleditor.TidyWriter.whitespace_elements = [
28136     'PRE','SCRIPT','NOSCRIPT','STYLE','TEXTAREA','VIDEO','AUDIO','IFRAME','OBJECT','CODE'
28137 ];/***
28138  * This is based loosely on tinymce 
28139  * @class Roo.htmleditor.TidyEntities
28140  * @static
28141  * https://github.com/thorn0/tinymce.html/blob/master/tinymce.html.js
28142  *
28143  * Not 100% sure this is actually used or needed.
28144  */
28145
28146 Roo.htmleditor.TidyEntities = {
28147     
28148     /**
28149      * initialize data..
28150      */
28151     init : function (){
28152      
28153         this.namedEntities = this.buildEntitiesLookup(this.namedEntitiesData, 32);
28154        
28155     },
28156
28157
28158     buildEntitiesLookup: function(items, radix) {
28159         var i, chr, entity, lookup = {};
28160         if (!items) {
28161             return {};
28162         }
28163         items = typeof(items) == 'string' ? items.split(',') : items;
28164         radix = radix || 10;
28165         // Build entities lookup table
28166         for (i = 0; i < items.length; i += 2) {
28167             chr = String.fromCharCode(parseInt(items[i], radix));
28168             // Only add non base entities
28169             if (!this.baseEntities[chr]) {
28170                 entity = '&' + items[i + 1] + ';';
28171                 lookup[chr] = entity;
28172                 lookup[entity] = chr;
28173             }
28174         }
28175         return lookup;
28176         
28177     },
28178     
28179     asciiMap : {
28180             128: '€',
28181             130: '‚',
28182             131: 'ƒ',
28183             132: '„',
28184             133: '…',
28185             134: '†',
28186             135: '‡',
28187             136: 'ˆ',
28188             137: '‰',
28189             138: 'Š',
28190             139: '‹',
28191             140: 'Œ',
28192             142: 'Ž',
28193             145: '‘',
28194             146: '’',
28195             147: '“',
28196             148: '”',
28197             149: '•',
28198             150: '–',
28199             151: '—',
28200             152: '˜',
28201             153: '™',
28202             154: 'š',
28203             155: '›',
28204             156: 'œ',
28205             158: 'ž',
28206             159: 'Ÿ'
28207     },
28208     // Raw entities
28209     baseEntities : {
28210         '"': '&quot;',
28211         // Needs to be escaped since the YUI compressor would otherwise break the code
28212         '\'': '&#39;',
28213         '<': '&lt;',
28214         '>': '&gt;',
28215         '&': '&amp;',
28216         '`': '&#96;'
28217     },
28218     // Reverse lookup table for raw entities
28219     reverseEntities : {
28220         '&lt;': '<',
28221         '&gt;': '>',
28222         '&amp;': '&',
28223         '&quot;': '"',
28224         '&apos;': '\''
28225     },
28226     
28227     attrsCharsRegExp : /[&<>\"\u0060\u007E-\uD7FF\uE000-\uFFEF]|[\uD800-\uDBFF][\uDC00-\uDFFF]/g,
28228     textCharsRegExp : /[<>&\u007E-\uD7FF\uE000-\uFFEF]|[\uD800-\uDBFF][\uDC00-\uDFFF]/g,
28229     rawCharsRegExp : /[<>&\"\']/g,
28230     entityRegExp : /&#([a-z0-9]+);?|&([a-z0-9]+);/gi,
28231     namedEntities  : false,
28232     namedEntitiesData : [ 
28233         '50',
28234         'nbsp',
28235         '51',
28236         'iexcl',
28237         '52',
28238         'cent',
28239         '53',
28240         'pound',
28241         '54',
28242         'curren',
28243         '55',
28244         'yen',
28245         '56',
28246         'brvbar',
28247         '57',
28248         'sect',
28249         '58',
28250         'uml',
28251         '59',
28252         'copy',
28253         '5a',
28254         'ordf',
28255         '5b',
28256         'laquo',
28257         '5c',
28258         'not',
28259         '5d',
28260         'shy',
28261         '5e',
28262         'reg',
28263         '5f',
28264         'macr',
28265         '5g',
28266         'deg',
28267         '5h',
28268         'plusmn',
28269         '5i',
28270         'sup2',
28271         '5j',
28272         'sup3',
28273         '5k',
28274         'acute',
28275         '5l',
28276         'micro',
28277         '5m',
28278         'para',
28279         '5n',
28280         'middot',
28281         '5o',
28282         'cedil',
28283         '5p',
28284         'sup1',
28285         '5q',
28286         'ordm',
28287         '5r',
28288         'raquo',
28289         '5s',
28290         'frac14',
28291         '5t',
28292         'frac12',
28293         '5u',
28294         'frac34',
28295         '5v',
28296         'iquest',
28297         '60',
28298         'Agrave',
28299         '61',
28300         'Aacute',
28301         '62',
28302         'Acirc',
28303         '63',
28304         'Atilde',
28305         '64',
28306         'Auml',
28307         '65',
28308         'Aring',
28309         '66',
28310         'AElig',
28311         '67',
28312         'Ccedil',
28313         '68',
28314         'Egrave',
28315         '69',
28316         'Eacute',
28317         '6a',
28318         'Ecirc',
28319         '6b',
28320         'Euml',
28321         '6c',
28322         'Igrave',
28323         '6d',
28324         'Iacute',
28325         '6e',
28326         'Icirc',
28327         '6f',
28328         'Iuml',
28329         '6g',
28330         'ETH',
28331         '6h',
28332         'Ntilde',
28333         '6i',
28334         'Ograve',
28335         '6j',
28336         'Oacute',
28337         '6k',
28338         'Ocirc',
28339         '6l',
28340         'Otilde',
28341         '6m',
28342         'Ouml',
28343         '6n',
28344         'times',
28345         '6o',
28346         'Oslash',
28347         '6p',
28348         'Ugrave',
28349         '6q',
28350         'Uacute',
28351         '6r',
28352         'Ucirc',
28353         '6s',
28354         'Uuml',
28355         '6t',
28356         'Yacute',
28357         '6u',
28358         'THORN',
28359         '6v',
28360         'szlig',
28361         '70',
28362         'agrave',
28363         '71',
28364         'aacute',
28365         '72',
28366         'acirc',
28367         '73',
28368         'atilde',
28369         '74',
28370         'auml',
28371         '75',
28372         'aring',
28373         '76',
28374         'aelig',
28375         '77',
28376         'ccedil',
28377         '78',
28378         'egrave',
28379         '79',
28380         'eacute',
28381         '7a',
28382         'ecirc',
28383         '7b',
28384         'euml',
28385         '7c',
28386         'igrave',
28387         '7d',
28388         'iacute',
28389         '7e',
28390         'icirc',
28391         '7f',
28392         'iuml',
28393         '7g',
28394         'eth',
28395         '7h',
28396         'ntilde',
28397         '7i',
28398         'ograve',
28399         '7j',
28400         'oacute',
28401         '7k',
28402         'ocirc',
28403         '7l',
28404         'otilde',
28405         '7m',
28406         'ouml',
28407         '7n',
28408         'divide',
28409         '7o',
28410         'oslash',
28411         '7p',
28412         'ugrave',
28413         '7q',
28414         'uacute',
28415         '7r',
28416         'ucirc',
28417         '7s',
28418         'uuml',
28419         '7t',
28420         'yacute',
28421         '7u',
28422         'thorn',
28423         '7v',
28424         'yuml',
28425         'ci',
28426         'fnof',
28427         'sh',
28428         'Alpha',
28429         'si',
28430         'Beta',
28431         'sj',
28432         'Gamma',
28433         'sk',
28434         'Delta',
28435         'sl',
28436         'Epsilon',
28437         'sm',
28438         'Zeta',
28439         'sn',
28440         'Eta',
28441         'so',
28442         'Theta',
28443         'sp',
28444         'Iota',
28445         'sq',
28446         'Kappa',
28447         'sr',
28448         'Lambda',
28449         'ss',
28450         'Mu',
28451         'st',
28452         'Nu',
28453         'su',
28454         'Xi',
28455         'sv',
28456         'Omicron',
28457         't0',
28458         'Pi',
28459         't1',
28460         'Rho',
28461         't3',
28462         'Sigma',
28463         't4',
28464         'Tau',
28465         't5',
28466         'Upsilon',
28467         't6',
28468         'Phi',
28469         't7',
28470         'Chi',
28471         't8',
28472         'Psi',
28473         't9',
28474         'Omega',
28475         'th',
28476         'alpha',
28477         'ti',
28478         'beta',
28479         'tj',
28480         'gamma',
28481         'tk',
28482         'delta',
28483         'tl',
28484         'epsilon',
28485         'tm',
28486         'zeta',
28487         'tn',
28488         'eta',
28489         'to',
28490         'theta',
28491         'tp',
28492         'iota',
28493         'tq',
28494         'kappa',
28495         'tr',
28496         'lambda',
28497         'ts',
28498         'mu',
28499         'tt',
28500         'nu',
28501         'tu',
28502         'xi',
28503         'tv',
28504         'omicron',
28505         'u0',
28506         'pi',
28507         'u1',
28508         'rho',
28509         'u2',
28510         'sigmaf',
28511         'u3',
28512         'sigma',
28513         'u4',
28514         'tau',
28515         'u5',
28516         'upsilon',
28517         'u6',
28518         'phi',
28519         'u7',
28520         'chi',
28521         'u8',
28522         'psi',
28523         'u9',
28524         'omega',
28525         'uh',
28526         'thetasym',
28527         'ui',
28528         'upsih',
28529         'um',
28530         'piv',
28531         '812',
28532         'bull',
28533         '816',
28534         'hellip',
28535         '81i',
28536         'prime',
28537         '81j',
28538         'Prime',
28539         '81u',
28540         'oline',
28541         '824',
28542         'frasl',
28543         '88o',
28544         'weierp',
28545         '88h',
28546         'image',
28547         '88s',
28548         'real',
28549         '892',
28550         'trade',
28551         '89l',
28552         'alefsym',
28553         '8cg',
28554         'larr',
28555         '8ch',
28556         'uarr',
28557         '8ci',
28558         'rarr',
28559         '8cj',
28560         'darr',
28561         '8ck',
28562         'harr',
28563         '8dl',
28564         'crarr',
28565         '8eg',
28566         'lArr',
28567         '8eh',
28568         'uArr',
28569         '8ei',
28570         'rArr',
28571         '8ej',
28572         'dArr',
28573         '8ek',
28574         'hArr',
28575         '8g0',
28576         'forall',
28577         '8g2',
28578         'part',
28579         '8g3',
28580         'exist',
28581         '8g5',
28582         'empty',
28583         '8g7',
28584         'nabla',
28585         '8g8',
28586         'isin',
28587         '8g9',
28588         'notin',
28589         '8gb',
28590         'ni',
28591         '8gf',
28592         'prod',
28593         '8gh',
28594         'sum',
28595         '8gi',
28596         'minus',
28597         '8gn',
28598         'lowast',
28599         '8gq',
28600         'radic',
28601         '8gt',
28602         'prop',
28603         '8gu',
28604         'infin',
28605         '8h0',
28606         'ang',
28607         '8h7',
28608         'and',
28609         '8h8',
28610         'or',
28611         '8h9',
28612         'cap',
28613         '8ha',
28614         'cup',
28615         '8hb',
28616         'int',
28617         '8hk',
28618         'there4',
28619         '8hs',
28620         'sim',
28621         '8i5',
28622         'cong',
28623         '8i8',
28624         'asymp',
28625         '8j0',
28626         'ne',
28627         '8j1',
28628         'equiv',
28629         '8j4',
28630         'le',
28631         '8j5',
28632         'ge',
28633         '8k2',
28634         'sub',
28635         '8k3',
28636         'sup',
28637         '8k4',
28638         'nsub',
28639         '8k6',
28640         'sube',
28641         '8k7',
28642         'supe',
28643         '8kl',
28644         'oplus',
28645         '8kn',
28646         'otimes',
28647         '8l5',
28648         'perp',
28649         '8m5',
28650         'sdot',
28651         '8o8',
28652         'lceil',
28653         '8o9',
28654         'rceil',
28655         '8oa',
28656         'lfloor',
28657         '8ob',
28658         'rfloor',
28659         '8p9',
28660         'lang',
28661         '8pa',
28662         'rang',
28663         '9ea',
28664         'loz',
28665         '9j0',
28666         'spades',
28667         '9j3',
28668         'clubs',
28669         '9j5',
28670         'hearts',
28671         '9j6',
28672         'diams',
28673         'ai',
28674         'OElig',
28675         'aj',
28676         'oelig',
28677         'b0',
28678         'Scaron',
28679         'b1',
28680         'scaron',
28681         'bo',
28682         'Yuml',
28683         'm6',
28684         'circ',
28685         'ms',
28686         'tilde',
28687         '802',
28688         'ensp',
28689         '803',
28690         'emsp',
28691         '809',
28692         'thinsp',
28693         '80c',
28694         'zwnj',
28695         '80d',
28696         'zwj',
28697         '80e',
28698         'lrm',
28699         '80f',
28700         'rlm',
28701         '80j',
28702         'ndash',
28703         '80k',
28704         'mdash',
28705         '80o',
28706         'lsquo',
28707         '80p',
28708         'rsquo',
28709         '80q',
28710         'sbquo',
28711         '80s',
28712         'ldquo',
28713         '80t',
28714         'rdquo',
28715         '80u',
28716         'bdquo',
28717         '810',
28718         'dagger',
28719         '811',
28720         'Dagger',
28721         '81g',
28722         'permil',
28723         '81p',
28724         'lsaquo',
28725         '81q',
28726         'rsaquo',
28727         '85c',
28728         'euro'
28729     ],
28730
28731          
28732     /**
28733      * Encodes the specified string using raw entities. This means only the required XML base entities will be encoded.
28734      *
28735      * @method encodeRaw
28736      * @param {String} text Text to encode.
28737      * @param {Boolean} attr Optional flag to specify if the text is attribute contents.
28738      * @return {String} Entity encoded text.
28739      */
28740     encodeRaw: function(text, attr)
28741     {
28742         var t = this;
28743         return text.replace(attr ? this.attrsCharsRegExp : this.textCharsRegExp, function(chr) {
28744             return t.baseEntities[chr] || chr;
28745         });
28746     },
28747     /**
28748      * Encoded the specified text with both the attributes and text entities. This function will produce larger text contents
28749      * since it doesn't know if the context is within a attribute or text node. This was added for compatibility
28750      * and is exposed as the DOMUtils.encode function.
28751      *
28752      * @method encodeAllRaw
28753      * @param {String} text Text to encode.
28754      * @return {String} Entity encoded text.
28755      */
28756     encodeAllRaw: function(text) {
28757         var t = this;
28758         return ('' + text).replace(this.rawCharsRegExp, function(chr) {
28759             return t.baseEntities[chr] || chr;
28760         });
28761     },
28762     /**
28763      * Encodes the specified string using numeric entities. The core entities will be
28764      * encoded as named ones but all non lower ascii characters will be encoded into numeric entities.
28765      *
28766      * @method encodeNumeric
28767      * @param {String} text Text to encode.
28768      * @param {Boolean} attr Optional flag to specify if the text is attribute contents.
28769      * @return {String} Entity encoded text.
28770      */
28771     encodeNumeric: function(text, attr) {
28772         var t = this;
28773         return text.replace(attr ? this.attrsCharsRegExp : this.textCharsRegExp, function(chr) {
28774             // Multi byte sequence convert it to a single entity
28775             if (chr.length > 1) {
28776                 return '&#' + (1024 * (chr.charCodeAt(0) - 55296) + (chr.charCodeAt(1) - 56320) + 65536) + ';';
28777             }
28778             return t.baseEntities[chr] || '&#' + chr.charCodeAt(0) + ';';
28779         });
28780     },
28781     /**
28782      * Encodes the specified string using named entities. The core entities will be encoded
28783      * as named ones but all non lower ascii characters will be encoded into named entities.
28784      *
28785      * @method encodeNamed
28786      * @param {String} text Text to encode.
28787      * @param {Boolean} attr Optional flag to specify if the text is attribute contents.
28788      * @param {Object} entities Optional parameter with entities to use.
28789      * @return {String} Entity encoded text.
28790      */
28791     encodeNamed: function(text, attr, entities) {
28792         var t = this;
28793         entities = entities || this.namedEntities;
28794         return text.replace(attr ? this.attrsCharsRegExp : this.textCharsRegExp, function(chr) {
28795             return t.baseEntities[chr] || entities[chr] || chr;
28796         });
28797     },
28798     /**
28799      * Returns an encode function based on the name(s) and it's optional entities.
28800      *
28801      * @method getEncodeFunc
28802      * @param {String} name Comma separated list of encoders for example named,numeric.
28803      * @param {String} entities Optional parameter with entities to use instead of the built in set.
28804      * @return {function} Encode function to be used.
28805      */
28806     getEncodeFunc: function(name, entities) {
28807         entities = this.buildEntitiesLookup(entities) || this.namedEntities;
28808         var t = this;
28809         function encodeNamedAndNumeric(text, attr) {
28810             return text.replace(attr ? t.attrsCharsRegExp : t.textCharsRegExp, function(chr) {
28811                 return t.baseEntities[chr] || entities[chr] || '&#' + chr.charCodeAt(0) + ';' || chr;
28812             });
28813         }
28814
28815         function encodeCustomNamed(text, attr) {
28816             return t.encodeNamed(text, attr, entities);
28817         }
28818         // Replace + with , to be compatible with previous TinyMCE versions
28819         name = this.makeMap(name.replace(/\+/g, ','));
28820         // Named and numeric encoder
28821         if (name.named && name.numeric) {
28822             return this.encodeNamedAndNumeric;
28823         }
28824         // Named encoder
28825         if (name.named) {
28826             // Custom names
28827             if (entities) {
28828                 return encodeCustomNamed;
28829             }
28830             return this.encodeNamed;
28831         }
28832         // Numeric
28833         if (name.numeric) {
28834             return this.encodeNumeric;
28835         }
28836         // Raw encoder
28837         return this.encodeRaw;
28838     },
28839     /**
28840      * Decodes the specified string, this will replace entities with raw UTF characters.
28841      *
28842      * @method decode
28843      * @param {String} text Text to entity decode.
28844      * @return {String} Entity decoded string.
28845      */
28846     decode: function(text)
28847     {
28848         var  t = this;
28849         return text.replace(this.entityRegExp, function(all, numeric) {
28850             if (numeric) {
28851                 numeric = 'x' === numeric.charAt(0).toLowerCase() ? parseInt(numeric.substr(1), 16) : parseInt(numeric, 10);
28852                 // Support upper UTF
28853                 if (numeric > 65535) {
28854                     numeric -= 65536;
28855                     return String.fromCharCode(55296 + (numeric >> 10), 56320 + (1023 & numeric));
28856                 }
28857                 return t.asciiMap[numeric] || String.fromCharCode(numeric);
28858             }
28859             return t.reverseEntities[all] || t.namedEntities[all] || t.nativeDecode(all);
28860         });
28861     },
28862     nativeDecode : function (text) {
28863         return text;
28864     },
28865     makeMap : function (items, delim, map) {
28866                 var i;
28867                 items = items || [];
28868                 delim = delim || ',';
28869                 if (typeof items == "string") {
28870                         items = items.split(delim);
28871                 }
28872                 map = map || {};
28873                 i = items.length;
28874                 while (i--) {
28875                         map[items[i]] = {};
28876                 }
28877                 return map;
28878         }
28879 };
28880     
28881     
28882     
28883 Roo.htmleditor.TidyEntities.init();
28884 /**
28885  * @class Roo.htmleditor.KeyEnter
28886  * Handle Enter press..
28887  * @cfg {Roo.HtmlEditorCore} core the editor.
28888  * @constructor
28889  * Create a new Filter.
28890  * @param {Object} config Configuration options
28891  */
28892
28893
28894
28895
28896
28897 Roo.htmleditor.KeyEnter = function(cfg) {
28898     Roo.apply(this, cfg);
28899     // this does not actually call walk as it's really just a abstract class
28900  
28901     Roo.get(this.core.doc.body).on('keypress', this.keypress, this);
28902 }
28903
28904 //Roo.htmleditor.KeyEnter.i = 0;
28905
28906
28907 Roo.htmleditor.KeyEnter.prototype = {
28908     
28909     core : false,
28910     
28911     keypress : function(e)
28912     {
28913         if (e.charCode != 13 && e.charCode != 10) {
28914             Roo.log([e.charCode,e]);
28915             return true;
28916         }
28917         e.preventDefault();
28918         // https://stackoverflow.com/questions/18552336/prevent-contenteditable-adding-div-on-enter-chrome
28919         var doc = this.core.doc;
28920           //add a new line
28921        
28922     
28923         var sel = this.core.getSelection();
28924         var range = sel.getRangeAt(0);
28925         var n = range.commonAncestorContainer;
28926         var pc = range.closest([ 'ol', 'ul']);
28927         var pli = range.closest('li');
28928         if (!pc || e.ctrlKey) {
28929             // on it list, or ctrl pressed.
28930             if (!e.ctrlKey) {
28931                 sel.insertNode('br', 'after'); 
28932             } else {
28933                 // only do this if we have ctrl key..
28934                 var br = doc.createElement('br');
28935                 br.className = 'clear';
28936                 br.setAttribute('style', 'clear: both');
28937                 sel.insertNode(br, 'after'); 
28938             }
28939             
28940          
28941             this.core.undoManager.addEvent();
28942             this.core.fireEditorEvent(e);
28943             return false;
28944         }
28945         
28946         // deal with <li> insetion
28947         if (pli.innerText.trim() == '' &&
28948             pli.previousSibling &&
28949             pli.previousSibling.nodeName == 'LI' &&
28950             pli.previousSibling.innerText.trim() ==  '') {
28951             pli.parentNode.removeChild(pli.previousSibling);
28952             sel.cursorAfter(pc);
28953             this.core.undoManager.addEvent();
28954             this.core.fireEditorEvent(e);
28955             return false;
28956         }
28957     
28958         var li = doc.createElement('LI');
28959         li.innerHTML = '&nbsp;';
28960         if (!pli || !pli.firstSibling) {
28961             pc.appendChild(li);
28962         } else {
28963             pli.parentNode.insertBefore(li, pli.firstSibling);
28964         }
28965         sel.cursorText (li.firstChild);
28966       
28967         this.core.undoManager.addEvent();
28968         this.core.fireEditorEvent(e);
28969
28970         return false;
28971         
28972     
28973         
28974         
28975          
28976     }
28977 };
28978      
28979 /**
28980  * @class Roo.htmleditor.Block
28981  * Base class for html editor blocks - do not use it directly .. extend it..
28982  * @cfg {DomElement} node The node to apply stuff to.
28983  * @cfg {String} friendly_name the name that appears in the context bar about this block
28984  * @cfg {Object} Context menu - see Roo.form.HtmlEditor.ToolbarContext
28985  
28986  * @constructor
28987  * Create a new Filter.
28988  * @param {Object} config Configuration options
28989  */
28990
28991 Roo.htmleditor.Block  = function(cfg)
28992 {
28993     // do nothing .. should not be called really.
28994 }
28995 /**
28996  * factory method to get the block from an element (using cache if necessary)
28997  * @static
28998  * @param {HtmlElement} the dom element
28999  */
29000 Roo.htmleditor.Block.factory = function(node)
29001 {
29002     var cc = Roo.htmleditor.Block.cache;
29003     var id = Roo.get(node).id;
29004     if (typeof(cc[id]) != 'undefined' && (!cc[id].node || cc[id].node.closest('body'))) {
29005         Roo.htmleditor.Block.cache[id].readElement(node);
29006         return Roo.htmleditor.Block.cache[id];
29007     }
29008     var db  = node.getAttribute('data-block');
29009     if (!db) {
29010         db = node.nodeName.toLowerCase().toUpperCaseFirst();
29011     }
29012     var cls = Roo.htmleditor['Block' + db];
29013     if (typeof(cls) == 'undefined') {
29014         //Roo.log(node.getAttribute('data-block'));
29015         Roo.log("OOps missing block : " + 'Block' + db);
29016         return false;
29017     }
29018     Roo.htmleditor.Block.cache[id] = new cls({ node: node });
29019     return Roo.htmleditor.Block.cache[id];  /// should trigger update element
29020 };
29021
29022 /**
29023  * initalize all Elements from content that are 'blockable'
29024  * @static
29025  * @param the body element
29026  */
29027 Roo.htmleditor.Block.initAll = function(body, type)
29028 {
29029     if (typeof(type) == 'undefined') {
29030         var ia = Roo.htmleditor.Block.initAll;
29031         ia(body,'table');
29032         ia(body,'td');
29033         ia(body,'figure');
29034         return;
29035     }
29036     Roo.each(Roo.get(body).query(type), function(e) {
29037         Roo.htmleditor.Block.factory(e);    
29038     },this);
29039 };
29040 // question goes here... do we need to clear out this cache sometimes?
29041 // or show we make it relivant to the htmleditor.
29042 Roo.htmleditor.Block.cache = {};
29043
29044 Roo.htmleditor.Block.prototype = {
29045     
29046     node : false,
29047     
29048      // used by context menu
29049     friendly_name : 'Based Block',
29050     
29051     // text for button to delete this element
29052     deleteTitle : false,
29053     
29054     context : false,
29055     /**
29056      * Update a node with values from this object
29057      * @param {DomElement} node
29058      */
29059     updateElement : function(node)
29060     {
29061         Roo.DomHelper.update(node === undefined ? this.node : node, this.toObject());
29062     },
29063      /**
29064      * convert to plain HTML for calling insertAtCursor..
29065      */
29066     toHTML : function()
29067     {
29068         return Roo.DomHelper.markup(this.toObject());
29069     },
29070     /**
29071      * used by readEleemnt to extract data from a node
29072      * may need improving as it's pretty basic
29073      
29074      * @param {DomElement} node
29075      * @param {String} tag - tag to find, eg. IMG ?? might be better to use DomQuery ?
29076      * @param {String} attribute (use html - for contents, style for using next param as style, or false to return the node)
29077      * @param {String} style the style property - eg. text-align
29078      */
29079     getVal : function(node, tag, attr, style)
29080     {
29081         var n = node;
29082         if (tag !== true && n.tagName != tag.toUpperCase()) {
29083             // in theory we could do figure[3] << 3rd figure? or some more complex search..?
29084             // but kiss for now.
29085             n = node.getElementsByTagName(tag).item(0);
29086         }
29087         if (!n) {
29088             return '';
29089         }
29090         if (attr === false) {
29091             return n;
29092         }
29093         if (attr == 'html') {
29094             return n.innerHTML;
29095         }
29096         if (attr == 'style') {
29097             return n.style[style]; 
29098         }
29099         
29100         return n.hasAttribute(attr) ? n.getAttribute(attr) : '';
29101             
29102     },
29103     /**
29104      * create a DomHelper friendly object - for use with 
29105      * Roo.DomHelper.markup / overwrite / etc..
29106      * (override this)
29107      */
29108     toObject : function()
29109     {
29110         return {};
29111     },
29112       /**
29113      * Read a node that has a 'data-block' property - and extract the values from it.
29114      * @param {DomElement} node - the node
29115      */
29116     readElement : function(node)
29117     {
29118         
29119     } 
29120     
29121     
29122 };
29123
29124  
29125
29126 /**
29127  * @class Roo.htmleditor.BlockFigure
29128  * Block that has an image and a figcaption
29129  * @cfg {String} image_src the url for the image
29130  * @cfg {String} align (left|right) alignment for the block default left
29131  * @cfg {String} caption the text to appear below  (and in the alt tag)
29132  * @cfg {String} caption_display (block|none) display or not the caption
29133  * @cfg {String|number} image_width the width of the image number or %?
29134  * @cfg {String|number} image_height the height of the image number or %?
29135  * 
29136  * @constructor
29137  * Create a new Filter.
29138  * @param {Object} config Configuration options
29139  */
29140
29141 Roo.htmleditor.BlockFigure = function(cfg)
29142 {
29143     if (cfg.node) {
29144         this.readElement(cfg.node);
29145         this.updateElement(cfg.node);
29146     }
29147     Roo.apply(this, cfg);
29148 }
29149 Roo.extend(Roo.htmleditor.BlockFigure, Roo.htmleditor.Block, {
29150  
29151     
29152     // setable values.
29153     image_src: '',
29154     align: 'center',
29155     caption : '',
29156     caption_display : 'block',
29157     width : '100%',
29158     cls : '',
29159     href: '',
29160     video_url : '',
29161     
29162     // margin: '2%', not used
29163     
29164     text_align: 'left', //   (left|right) alignment for the text caption default left. - not used at present
29165
29166     
29167     // used by context menu
29168     friendly_name : 'Image with caption',
29169     deleteTitle : "Delete Image and Caption",
29170     
29171     contextMenu : function(toolbar)
29172     {
29173         
29174         var block = function() {
29175             return Roo.htmleditor.Block.factory(toolbar.tb.selectedNode);
29176         };
29177         
29178         
29179         var rooui =  typeof(Roo.bootstrap) == 'undefined' ? Roo : Roo.bootstrap;
29180         
29181         var syncValue = toolbar.editorcore.syncValue;
29182         
29183         var fields = {};
29184         
29185         return [
29186              {
29187                 xtype : 'TextItem',
29188                 text : "Source: ",
29189                 xns : rooui.Toolbar  //Boostrap?
29190             },
29191             {
29192                 xtype : 'Button',
29193                 text: 'Change Image URL',
29194                  
29195                 listeners : {
29196                     click: function (btn, state)
29197                     {
29198                         var b = block();
29199                         
29200                         Roo.MessageBox.show({
29201                             title : "Image Source URL",
29202                             msg : "Enter the url for the image",
29203                             buttons: Roo.MessageBox.OKCANCEL,
29204                             fn: function(btn, val){
29205                                 if (btn != 'ok') {
29206                                     return;
29207                                 }
29208                                 b.image_src = val;
29209                                 b.updateElement();
29210                                 syncValue();
29211                                 toolbar.editorcore.onEditorEvent();
29212                             },
29213                             minWidth:250,
29214                             prompt:true,
29215                             //multiline: multiline,
29216                             modal : true,
29217                             value : b.image_src
29218                         });
29219                     }
29220                 },
29221                 xns : rooui.Toolbar
29222             },
29223          
29224             {
29225                 xtype : 'Button',
29226                 text: 'Change Link URL',
29227                  
29228                 listeners : {
29229                     click: function (btn, state)
29230                     {
29231                         var b = block();
29232                         
29233                         Roo.MessageBox.show({
29234                             title : "Link URL",
29235                             msg : "Enter the url for the link - leave blank to have no link",
29236                             buttons: Roo.MessageBox.OKCANCEL,
29237                             fn: function(btn, val){
29238                                 if (btn != 'ok') {
29239                                     return;
29240                                 }
29241                                 b.href = val;
29242                                 b.updateElement();
29243                                 syncValue();
29244                                 toolbar.editorcore.onEditorEvent();
29245                             },
29246                             minWidth:250,
29247                             prompt:true,
29248                             //multiline: multiline,
29249                             modal : true,
29250                             value : b.href
29251                         });
29252                     }
29253                 },
29254                 xns : rooui.Toolbar
29255             },
29256             {
29257                 xtype : 'Button',
29258                 text: 'Show Video URL',
29259                  
29260                 listeners : {
29261                     click: function (btn, state)
29262                     {
29263                         Roo.MessageBox.alert("Video URL",
29264                             block().video_url == '' ? 'This image is not linked ot a video' :
29265                                 'The image is linked to: <a target="_new" href="' + block().video_url + '">' + block().video_url + '</a>');
29266                     }
29267                 },
29268                 xns : rooui.Toolbar
29269             },
29270             
29271             
29272             {
29273                 xtype : 'TextItem',
29274                 text : "Width: ",
29275                 xns : rooui.Toolbar  //Boostrap?
29276             },
29277             {
29278                 xtype : 'ComboBox',
29279                 allowBlank : false,
29280                 displayField : 'val',
29281                 editable : true,
29282                 listWidth : 100,
29283                 triggerAction : 'all',
29284                 typeAhead : true,
29285                 valueField : 'val',
29286                 width : 70,
29287                 name : 'width',
29288                 listeners : {
29289                     select : function (combo, r, index)
29290                     {
29291                         toolbar.editorcore.selectNode(toolbar.tb.selectedNode);
29292                         var b = block();
29293                         b.width = r.get('val');
29294                         b.updateElement();
29295                         syncValue();
29296                         toolbar.editorcore.onEditorEvent();
29297                     }
29298                 },
29299                 xns : rooui.form,
29300                 store : {
29301                     xtype : 'SimpleStore',
29302                     data : [
29303                         ['100%'],
29304                         ['80%'],
29305                         ['50%'],
29306                         ['20%'],
29307                         ['10%']
29308                     ],
29309                     fields : [ 'val'],
29310                     xns : Roo.data
29311                 }
29312             },
29313             {
29314                 xtype : 'TextItem',
29315                 text : "Align: ",
29316                 xns : rooui.Toolbar  //Boostrap?
29317             },
29318             {
29319                 xtype : 'ComboBox',
29320                 allowBlank : false,
29321                 displayField : 'val',
29322                 editable : true,
29323                 listWidth : 100,
29324                 triggerAction : 'all',
29325                 typeAhead : true,
29326                 valueField : 'val',
29327                 width : 70,
29328                 name : 'align',
29329                 listeners : {
29330                     select : function (combo, r, index)
29331                     {
29332                         toolbar.editorcore.selectNode(toolbar.tb.selectedNode);
29333                         var b = block();
29334                         b.align = r.get('val');
29335                         b.updateElement();
29336                         syncValue();
29337                         toolbar.editorcore.onEditorEvent();
29338                     }
29339                 },
29340                 xns : rooui.form,
29341                 store : {
29342                     xtype : 'SimpleStore',
29343                     data : [
29344                         ['left'],
29345                         ['right'],
29346                         ['center']
29347                     ],
29348                     fields : [ 'val'],
29349                     xns : Roo.data
29350                 }
29351             },
29352             
29353               
29354             {
29355                 xtype : 'Button',
29356                 text: 'Hide Caption',
29357                 name : 'caption_display',
29358                 pressed : false,
29359                 enableToggle : true,
29360                 setValue : function(v) {
29361                     // this trigger toggle.
29362                      
29363                     this.setText(v ? "Hide Caption" : "Show Caption");
29364                     this.setPressed(v != 'block');
29365                 },
29366                 listeners : {
29367                     toggle: function (btn, state)
29368                     {
29369                         var b  = block();
29370                         b.caption_display = b.caption_display == 'block' ? 'none' : 'block';
29371                         this.setText(b.caption_display == 'block' ? "Hide Caption" : "Show Caption");
29372                         b.updateElement();
29373                         syncValue();
29374                         toolbar.editorcore.selectNode(toolbar.tb.selectedNode);
29375                         toolbar.editorcore.onEditorEvent();
29376                     }
29377                 },
29378                 xns : rooui.Toolbar
29379             }
29380         ];
29381         
29382     },
29383     /**
29384      * create a DomHelper friendly object - for use with
29385      * Roo.DomHelper.markup / overwrite / etc..
29386      */
29387     toObject : function()
29388     {
29389         var d = document.createElement('div');
29390         d.innerHTML = this.caption;
29391         
29392         var m = this.width != '100%' && this.align == 'center' ? '0 auto' : 0; 
29393         
29394         var iw = this.align == 'center' ? this.width : '100%';
29395         var img =   {
29396             tag : 'img',
29397             contenteditable : 'false',
29398             src : this.image_src,
29399             alt : d.innerText.replace(/\n/g, " ").replace(/\s+/g, ' ').trim(), // removeHTML and reduce spaces..
29400             style: {
29401                 width : iw,
29402                 maxWidth : iw + ' !important', // this is not getting rendered?
29403                 margin : m  
29404                 
29405             }
29406         };
29407         /*
29408         '<div class="{0}" width="420" height="315" src="{1}" frameborder="0" allowfullscreen>' +
29409                     '<a href="{2}">' + 
29410                         '<img class="{0}-thumbnail" src="{3}/Images/{4}/{5}#image-{4}" />' + 
29411                     '</a>' + 
29412                 '</div>',
29413         */
29414                 
29415         if (this.href.length > 0) {
29416             img = {
29417                 tag : 'a',
29418                 href: this.href,
29419                 contenteditable : 'true',
29420                 cn : [
29421                     img
29422                 ]
29423             };
29424         }
29425         
29426         
29427         if (this.video_url.length > 0) {
29428             img = {
29429                 tag : 'div',
29430                 cls : this.cls,
29431                 frameborder : 0,
29432                 allowfullscreen : true,
29433                 width : 420,  // these are for video tricks - that we replace the outer
29434                 height : 315,
29435                 src : this.video_url,
29436                 cn : [
29437                     img
29438                 ]
29439             };
29440         }
29441         // we remove caption totally if its hidden... - will delete data.. but otherwise we end up with fake caption
29442         var captionhtml = this.caption_display == 'none' ? '' : (this.caption.length ? this.caption : "Caption");
29443         
29444   
29445         var ret =   {
29446             tag: 'figure',
29447             'data-block' : 'Figure',
29448             'data-width' : this.width,
29449             'data-caption' : this.caption, 
29450             contenteditable : 'false',
29451             
29452             style : {
29453                 display: 'block',
29454                 float :  this.align ,
29455                 maxWidth :  this.align == 'center' ? '100% !important' : (this.width + ' !important'),
29456                 width : this.align == 'center' ? '100%' : this.width,
29457                 margin:  '0px',
29458                 padding: this.align == 'center' ? '0' : '0 10px' ,
29459                 textAlign : this.align   // seems to work for email..
29460                 
29461             },
29462            
29463             
29464             align : this.align,
29465             cn : [
29466                 img,
29467               
29468                 {
29469                     tag: 'figcaption',
29470                     'data-display' : this.caption_display,
29471                     style : {
29472                         textAlign : 'left',
29473                         fontSize : '16px',
29474                         lineHeight : '24px',
29475                         display : this.caption_display,
29476                         maxWidth : (this.align == 'center' ?  this.width : '100%' ) + ' !important',
29477                         margin: m,
29478                         width: this.align == 'center' ?  this.width : '100%' 
29479                     
29480                          
29481                     },
29482                     cls : this.cls.length > 0 ? (this.cls  + '-thumbnail' ) : '',
29483                     cn : [
29484                         {
29485                             tag: 'div',
29486                             style  : {
29487                                 marginTop : '16px',
29488                                 textAlign : 'left'
29489                             },
29490                             align: 'left',
29491                             cn : [
29492                                 {
29493                                     // we can not rely on yahoo syndication to use CSS elements - so have to use  '<i>' to encase stuff.
29494                                     tag : 'i',
29495                                     contenteditable : true,
29496                                     html : captionhtml
29497                                 }
29498                                 
29499                             ]
29500                         }
29501                         
29502                     ]
29503                     
29504                 }
29505             ]
29506         };
29507         return ret;
29508          
29509     },
29510     
29511     readElement : function(node)
29512     {
29513         // this should not really come from the link...
29514         this.video_url = this.getVal(node, 'div', 'src');
29515         this.cls = this.getVal(node, 'div', 'class');
29516         this.href = this.getVal(node, 'a', 'href');
29517         
29518         
29519         this.image_src = this.getVal(node, 'img', 'src');
29520          
29521         this.align = this.getVal(node, 'figure', 'align');
29522         
29523         /// not really used - as hidden captions do not store the content here..
29524         var figcaption = this.getVal(node, 'figcaption', false);
29525         if (figcaption !== '') {
29526             this.caption = this.getVal(figcaption, 'i', 'html');
29527         }
29528         
29529
29530         this.caption_display = this.getVal(node, 'figcaption', 'data-display');
29531         var dc = this.getVal(node, true, 'data-caption');
29532         if (dc && dc.length) {
29533             this.caption = dc;
29534         }
29535         //this.text_align = this.getVal(node, 'figcaption', 'style','text-align');
29536         this.width = this.getVal(node, true, 'data-width');
29537         //this.margin = this.getVal(node, 'figure', 'style', 'margin');
29538         
29539     },
29540     removeNode : function()
29541     {
29542         return this.node;
29543     }
29544     
29545   
29546    
29547      
29548     
29549     
29550     
29551     
29552 })
29553
29554  
29555
29556 /**
29557  * @class Roo.htmleditor.BlockTable
29558  * Block that manages a table
29559  * 
29560  * @constructor
29561  * Create a new Filter.
29562  * @param {Object} config Configuration options
29563  */
29564
29565 Roo.htmleditor.BlockTable = function(cfg)
29566 {
29567     if (cfg.node) {
29568         this.readElement(cfg.node);
29569         this.updateElement(cfg.node);
29570     }
29571     Roo.apply(this, cfg);
29572     if (!cfg.node) {
29573         this.rows = [];
29574         for(var r = 0; r < this.no_row; r++) {
29575             this.rows[r] = [];
29576             for(var c = 0; c < this.no_col; c++) {
29577                 this.rows[r][c] = this.emptyCell();
29578             }
29579         }
29580     }
29581     
29582     
29583 }
29584 Roo.extend(Roo.htmleditor.BlockTable, Roo.htmleditor.Block, {
29585  
29586     rows : false,
29587     no_col : 1,
29588     no_row : 1,
29589     
29590     
29591     width: '100%',
29592     
29593     // used by context menu
29594     friendly_name : 'Table',
29595     deleteTitle : 'Delete Table',
29596     // context menu is drawn once..
29597     
29598     contextMenu : function(toolbar)
29599     {
29600         
29601         var block = function() {
29602             return Roo.htmleditor.Block.factory(toolbar.tb.selectedNode);
29603         };
29604         
29605         
29606         var rooui =  typeof(Roo.bootstrap) == 'undefined' ? Roo : Roo.bootstrap;
29607         
29608         var syncValue = toolbar.editorcore.syncValue;
29609         
29610         var fields = {};
29611         
29612         return [
29613             {
29614                 xtype : 'TextItem',
29615                 text : "Width: ",
29616                 xns : rooui.Toolbar  //Boostrap?
29617             },
29618             {
29619                 xtype : 'ComboBox',
29620                 allowBlank : false,
29621                 displayField : 'val',
29622                 editable : true,
29623                 listWidth : 100,
29624                 triggerAction : 'all',
29625                 typeAhead : true,
29626                 valueField : 'val',
29627                 width : 100,
29628                 name : 'width',
29629                 listeners : {
29630                     select : function (combo, r, index)
29631                     {
29632                         toolbar.editorcore.selectNode(toolbar.tb.selectedNode);
29633                         var b = block();
29634                         b.width = r.get('val');
29635                         b.updateElement();
29636                         syncValue();
29637                         toolbar.editorcore.onEditorEvent();
29638                     }
29639                 },
29640                 xns : rooui.form,
29641                 store : {
29642                     xtype : 'SimpleStore',
29643                     data : [
29644                         ['100%'],
29645                         ['auto']
29646                     ],
29647                     fields : [ 'val'],
29648                     xns : Roo.data
29649                 }
29650             },
29651             // -------- Cols
29652             
29653             {
29654                 xtype : 'TextItem',
29655                 text : "Columns: ",
29656                 xns : rooui.Toolbar  //Boostrap?
29657             },
29658          
29659             {
29660                 xtype : 'Button',
29661                 text: '-',
29662                 listeners : {
29663                     click : function (_self, e)
29664                     {
29665                         toolbar.editorcore.selectNode(toolbar.tb.selectedNode);
29666                         block().removeColumn();
29667                         syncValue();
29668                         toolbar.editorcore.onEditorEvent();
29669                     }
29670                 },
29671                 xns : rooui.Toolbar
29672             },
29673             {
29674                 xtype : 'Button',
29675                 text: '+',
29676                 listeners : {
29677                     click : function (_self, e)
29678                     {
29679                         toolbar.editorcore.selectNode(toolbar.tb.selectedNode);
29680                         block().addColumn();
29681                         syncValue();
29682                         toolbar.editorcore.onEditorEvent();
29683                     }
29684                 },
29685                 xns : rooui.Toolbar
29686             },
29687             // -------- ROWS
29688             {
29689                 xtype : 'TextItem',
29690                 text : "Rows: ",
29691                 xns : rooui.Toolbar  //Boostrap?
29692             },
29693          
29694             {
29695                 xtype : 'Button',
29696                 text: '-',
29697                 listeners : {
29698                     click : function (_self, e)
29699                     {
29700                         toolbar.editorcore.selectNode(toolbar.tb.selectedNode);
29701                         block().removeRow();
29702                         syncValue();
29703                         toolbar.editorcore.onEditorEvent();
29704                     }
29705                 },
29706                 xns : rooui.Toolbar
29707             },
29708             {
29709                 xtype : 'Button',
29710                 text: '+',
29711                 listeners : {
29712                     click : function (_self, e)
29713                     {
29714                         block().addRow();
29715                         syncValue();
29716                         toolbar.editorcore.onEditorEvent();
29717                     }
29718                 },
29719                 xns : rooui.Toolbar
29720             },
29721             // -------- ROWS
29722             {
29723                 xtype : 'Button',
29724                 text: 'Reset Column Widths',
29725                 listeners : {
29726                     
29727                     click : function (_self, e)
29728                     {
29729                         block().resetWidths();
29730                         syncValue();
29731                         toolbar.editorcore.onEditorEvent();
29732                     }
29733                 },
29734                 xns : rooui.Toolbar
29735             } 
29736             
29737             
29738             
29739         ];
29740         
29741     },
29742     
29743     
29744   /**
29745      * create a DomHelper friendly object - for use with
29746      * Roo.DomHelper.markup / overwrite / etc..
29747      * ?? should it be called with option to hide all editing features?
29748      */
29749     toObject : function()
29750     {
29751         
29752         var ret = {
29753             tag : 'table',
29754             contenteditable : 'false', // this stops cell selection from picking the table.
29755             'data-block' : 'Table',
29756             style : {
29757                 width:  this.width,
29758                 border : 'solid 1px #000', // ??? hard coded?
29759                 'border-collapse' : 'collapse' 
29760             },
29761             cn : [
29762                 { tag : 'tbody' , cn : [] }
29763             ]
29764         };
29765         
29766         // do we have a head = not really 
29767         var ncols = 0;
29768         Roo.each(this.rows, function( row ) {
29769             var tr = {
29770                 tag: 'tr',
29771                 style : {
29772                     margin: '6px',
29773                     border : 'solid 1px #000',
29774                     textAlign : 'left' 
29775                 },
29776                 cn : [ ]
29777             };
29778             
29779             ret.cn[0].cn.push(tr);
29780             // does the row have any properties? ?? height?
29781             var nc = 0;
29782             Roo.each(row, function( cell ) {
29783                 
29784                 var td = {
29785                     tag : 'td',
29786                     contenteditable :  'true',
29787                     'data-block' : 'Td',
29788                     html : cell.html,
29789                     style : cell.style
29790                 };
29791                 if (cell.colspan > 1) {
29792                     td.colspan = cell.colspan ;
29793                     nc += cell.colspan;
29794                 } else {
29795                     nc++;
29796                 }
29797                 if (cell.rowspan > 1) {
29798                     td.rowspan = cell.rowspan ;
29799                 }
29800                 
29801                 
29802                 // widths ?
29803                 tr.cn.push(td);
29804                     
29805                 
29806             }, this);
29807             ncols = Math.max(nc, ncols);
29808             
29809             
29810         }, this);
29811         // add the header row..
29812         
29813         ncols++;
29814          
29815         
29816         return ret;
29817          
29818     },
29819     
29820     readElement : function(node)
29821     {
29822         node  = node ? node : this.node ;
29823         this.width = this.getVal(node, true, 'style', 'width') || '100%';
29824         
29825         this.rows = [];
29826         this.no_row = 0;
29827         var trs = Array.from(node.rows);
29828         trs.forEach(function(tr) {
29829             var row =  [];
29830             this.rows.push(row);
29831             
29832             this.no_row++;
29833             var no_column = 0;
29834             Array.from(tr.cells).forEach(function(td) {
29835                 
29836                 var add = {
29837                     colspan : td.hasAttribute('colspan') ? td.getAttribute('colspan')*1 : 1,
29838                     rowspan : td.hasAttribute('rowspan') ? td.getAttribute('rowspan')*1 : 1,
29839                     style : td.hasAttribute('style') ? td.getAttribute('style') : '',
29840                     html : td.innerHTML
29841                 };
29842                 no_column += add.colspan;
29843                      
29844                 
29845                 row.push(add);
29846                 
29847                 
29848             },this);
29849             this.no_col = Math.max(this.no_col, no_column);
29850             
29851             
29852         },this);
29853         
29854         
29855     },
29856     normalizeRows: function()
29857     {
29858         var ret= [];
29859         var rid = -1;
29860         this.rows.forEach(function(row) {
29861             rid++;
29862             ret[rid] = [];
29863             row = this.normalizeRow(row);
29864             var cid = 0;
29865             row.forEach(function(c) {
29866                 while (typeof(ret[rid][cid]) != 'undefined') {
29867                     cid++;
29868                 }
29869                 if (typeof(ret[rid]) == 'undefined') {
29870                     ret[rid] = [];
29871                 }
29872                 ret[rid][cid] = c;
29873                 c.row = rid;
29874                 c.col = cid;
29875                 if (c.rowspan < 2) {
29876                     return;
29877                 }
29878                 
29879                 for(var i = 1 ;i < c.rowspan; i++) {
29880                     if (typeof(ret[rid+i]) == 'undefined') {
29881                         ret[rid+i] = [];
29882                     }
29883                     ret[rid+i][cid] = c;
29884                 }
29885             });
29886         }, this);
29887         return ret;
29888     
29889     },
29890     
29891     normalizeRow: function(row)
29892     {
29893         var ret= [];
29894         row.forEach(function(c) {
29895             if (c.colspan < 2) {
29896                 ret.push(c);
29897                 return;
29898             }
29899             for(var i =0 ;i < c.colspan; i++) {
29900                 ret.push(c);
29901             }
29902         });
29903         return ret;
29904     
29905     },
29906     
29907     deleteColumn : function(sel)
29908     {
29909         if (!sel || sel.type != 'col') {
29910             return;
29911         }
29912         if (this.no_col < 2) {
29913             return;
29914         }
29915         
29916         this.rows.forEach(function(row) {
29917             var cols = this.normalizeRow(row);
29918             var col = cols[sel.col];
29919             if (col.colspan > 1) {
29920                 col.colspan --;
29921             } else {
29922                 row.remove(col);
29923             }
29924             
29925         }, this);
29926         this.no_col--;
29927         
29928     },
29929     removeColumn : function()
29930     {
29931         this.deleteColumn({
29932             type: 'col',
29933             col : this.no_col-1
29934         });
29935         this.updateElement();
29936     },
29937     
29938      
29939     addColumn : function()
29940     {
29941         
29942         this.rows.forEach(function(row) {
29943             row.push(this.emptyCell());
29944            
29945         }, this);
29946         this.updateElement();
29947     },
29948     
29949     deleteRow : function(sel)
29950     {
29951         if (!sel || sel.type != 'row') {
29952             return;
29953         }
29954         
29955         if (this.no_row < 2) {
29956             return;
29957         }
29958         
29959         var rows = this.normalizeRows();
29960         
29961         
29962         rows[sel.row].forEach(function(col) {
29963             if (col.rowspan > 1) {
29964                 col.rowspan--;
29965             } else {
29966                 col.remove = 1; // flage it as removed.
29967             }
29968             
29969         }, this);
29970         var newrows = [];
29971         this.rows.forEach(function(row) {
29972             newrow = [];
29973             row.forEach(function(c) {
29974                 if (typeof(c.remove) == 'undefined') {
29975                     newrow.push(c);
29976                 }
29977                 
29978             });
29979             if (newrow.length > 0) {
29980                 newrows.push(row);
29981             }
29982         });
29983         this.rows =  newrows;
29984         
29985         
29986         
29987         this.no_row--;
29988         this.updateElement();
29989         
29990     },
29991     removeRow : function()
29992     {
29993         this.deleteRow({
29994             type: 'row',
29995             row : this.no_row-1
29996         });
29997         
29998     },
29999     
30000      
30001     addRow : function()
30002     {
30003         
30004         var row = [];
30005         for (var i = 0; i < this.no_col; i++ ) {
30006             
30007             row.push(this.emptyCell());
30008            
30009         }
30010         this.rows.push(row);
30011         this.updateElement();
30012         
30013     },
30014      
30015     // the default cell object... at present...
30016     emptyCell : function() {
30017         return (new Roo.htmleditor.BlockTd({})).toObject();
30018         
30019      
30020     },
30021     
30022     removeNode : function()
30023     {
30024         return this.node;
30025     },
30026     
30027     
30028     
30029     resetWidths : function()
30030     {
30031         Array.from(this.node.getElementsByTagName('td')).forEach(function(n) {
30032             var nn = Roo.htmleditor.Block.factory(n);
30033             nn.width = '';
30034             nn.updateElement(n);
30035         });
30036     }
30037     
30038     
30039     
30040     
30041 })
30042
30043 /**
30044  *
30045  * editing a TD?
30046  *
30047  * since selections really work on the table cell, then editing really should work from there
30048  *
30049  * The original plan was to support merging etc... - but that may not be needed yet..
30050  *
30051  * So this simple version will support:
30052  *   add/remove cols
30053  *   adjust the width +/-
30054  *   reset the width...
30055  *   
30056  *
30057  */
30058
30059
30060  
30061
30062 /**
30063  * @class Roo.htmleditor.BlockTable
30064  * Block that manages a table
30065  * 
30066  * @constructor
30067  * Create a new Filter.
30068  * @param {Object} config Configuration options
30069  */
30070
30071 Roo.htmleditor.BlockTd = function(cfg)
30072 {
30073     if (cfg.node) {
30074         this.readElement(cfg.node);
30075         this.updateElement(cfg.node);
30076     }
30077     Roo.apply(this, cfg);
30078      
30079     
30080     
30081 }
30082 Roo.extend(Roo.htmleditor.BlockTd, Roo.htmleditor.Block, {
30083  
30084     node : false,
30085     
30086     width: '',
30087     textAlign : 'left',
30088     valign : 'top',
30089     
30090     colspan : 1,
30091     rowspan : 1,
30092     
30093     
30094     // used by context menu
30095     friendly_name : 'Table Cell',
30096     deleteTitle : false, // use our customer delete
30097     
30098     // context menu is drawn once..
30099     
30100     contextMenu : function(toolbar)
30101     {
30102         
30103         var cell = function() {
30104             return Roo.htmleditor.Block.factory(toolbar.tb.selectedNode);
30105         };
30106         
30107         var table = function() {
30108             return Roo.htmleditor.Block.factory(toolbar.tb.selectedNode.closest('table'));
30109         };
30110         
30111         var lr = false;
30112         var saveSel = function()
30113         {
30114             lr = toolbar.editorcore.getSelection().getRangeAt(0);
30115         }
30116         var restoreSel = function()
30117         {
30118             if (lr) {
30119                 (function() {
30120                     toolbar.editorcore.focus();
30121                     var cr = toolbar.editorcore.getSelection();
30122                     cr.removeAllRanges();
30123                     cr.addRange(lr);
30124                     toolbar.editorcore.onEditorEvent();
30125                 }).defer(10, this);
30126                 
30127                 
30128             }
30129         }
30130         
30131         var rooui =  typeof(Roo.bootstrap) == 'undefined' ? Roo : Roo.bootstrap;
30132         
30133         var syncValue = toolbar.editorcore.syncValue;
30134         
30135         var fields = {};
30136         
30137         return [
30138             {
30139                 xtype : 'Button',
30140                 text : 'Edit Table',
30141                 listeners : {
30142                     click : function() {
30143                         var t = toolbar.tb.selectedNode.closest('table');
30144                         toolbar.editorcore.selectNode(t);
30145                         toolbar.editorcore.onEditorEvent();                        
30146                     }
30147                 }
30148                 
30149             },
30150               
30151            
30152              
30153             {
30154                 xtype : 'TextItem',
30155                 text : "Column Width: ",
30156                  xns : rooui.Toolbar 
30157                
30158             },
30159             {
30160                 xtype : 'Button',
30161                 text: '-',
30162                 listeners : {
30163                     click : function (_self, e)
30164                     {
30165                         toolbar.editorcore.selectNode(toolbar.tb.selectedNode);
30166                         cell().shrinkColumn();
30167                         syncValue();
30168                          toolbar.editorcore.onEditorEvent();
30169                     }
30170                 },
30171                 xns : rooui.Toolbar
30172             },
30173             {
30174                 xtype : 'Button',
30175                 text: '+',
30176                 listeners : {
30177                     click : function (_self, e)
30178                     {
30179                         toolbar.editorcore.selectNode(toolbar.tb.selectedNode);
30180                         cell().growColumn();
30181                         syncValue();
30182                         toolbar.editorcore.onEditorEvent();
30183                     }
30184                 },
30185                 xns : rooui.Toolbar
30186             },
30187             
30188             {
30189                 xtype : 'TextItem',
30190                 text : "Vertical Align: ",
30191                 xns : rooui.Toolbar  //Boostrap?
30192             },
30193             {
30194                 xtype : 'ComboBox',
30195                 allowBlank : false,
30196                 displayField : 'val',
30197                 editable : true,
30198                 listWidth : 100,
30199                 triggerAction : 'all',
30200                 typeAhead : true,
30201                 valueField : 'val',
30202                 width : 100,
30203                 name : 'valign',
30204                 listeners : {
30205                     select : function (combo, r, index)
30206                     {
30207                         toolbar.editorcore.selectNode(toolbar.tb.selectedNode);
30208                         var b = cell();
30209                         b.valign = r.get('val');
30210                         b.updateElement();
30211                         syncValue();
30212                         toolbar.editorcore.onEditorEvent();
30213                     }
30214                 },
30215                 xns : rooui.form,
30216                 store : {
30217                     xtype : 'SimpleStore',
30218                     data : [
30219                         ['top'],
30220                         ['middle'],
30221                         ['bottom'] // there are afew more... 
30222                     ],
30223                     fields : [ 'val'],
30224                     xns : Roo.data
30225                 }
30226             },
30227             
30228             {
30229                 xtype : 'TextItem',
30230                 text : "Merge Cells: ",
30231                  xns : rooui.Toolbar 
30232                
30233             },
30234             
30235             
30236             {
30237                 xtype : 'Button',
30238                 text: 'Right',
30239                 listeners : {
30240                     click : function (_self, e)
30241                     {
30242                         toolbar.editorcore.selectNode(toolbar.tb.selectedNode);
30243                         cell().mergeRight();
30244                         //block().growColumn();
30245                         syncValue();
30246                         toolbar.editorcore.onEditorEvent();
30247                     }
30248                 },
30249                 xns : rooui.Toolbar
30250             },
30251              
30252             {
30253                 xtype : 'Button',
30254                 text: 'Below',
30255                 listeners : {
30256                     click : function (_self, e)
30257                     {
30258                         toolbar.editorcore.selectNode(toolbar.tb.selectedNode);
30259                         cell().mergeBelow();
30260                         //block().growColumn();
30261                         syncValue();
30262                         toolbar.editorcore.onEditorEvent();
30263                     }
30264                 },
30265                 xns : rooui.Toolbar
30266             },
30267             {
30268                 xtype : 'TextItem',
30269                 text : "| ",
30270                  xns : rooui.Toolbar 
30271                
30272             },
30273             
30274             {
30275                 xtype : 'Button',
30276                 text: 'Split',
30277                 listeners : {
30278                     click : function (_self, e)
30279                     {
30280                         //toolbar.editorcore.selectNode(toolbar.tb.selectedNode);
30281                         cell().split();
30282                         syncValue();
30283                         toolbar.editorcore.selectNode(toolbar.tb.selectedNode);
30284                         toolbar.editorcore.onEditorEvent();
30285                                              
30286                     }
30287                 },
30288                 xns : rooui.Toolbar
30289             },
30290             {
30291                 xtype : 'Fill',
30292                 xns : rooui.Toolbar 
30293                
30294             },
30295         
30296           
30297             {
30298                 xtype : 'Button',
30299                 text: 'Delete',
30300                  
30301                 xns : rooui.Toolbar,
30302                 menu : {
30303                     xtype : 'Menu',
30304                     xns : rooui.menu,
30305                     items : [
30306                         {
30307                             xtype : 'Item',
30308                             html: 'Column',
30309                             listeners : {
30310                                 click : function (_self, e)
30311                                 {
30312                                     var t = table();
30313                                     
30314                                     cell().deleteColumn();
30315                                     syncValue();
30316                                     toolbar.editorcore.selectNode(t.node);
30317                                     toolbar.editorcore.onEditorEvent();   
30318                                 }
30319                             },
30320                             xns : rooui.menu
30321                         },
30322                         {
30323                             xtype : 'Item',
30324                             html: 'Row',
30325                             listeners : {
30326                                 click : function (_self, e)
30327                                 {
30328                                     var t = table();
30329                                     cell().deleteRow();
30330                                     syncValue();
30331                                     
30332                                     toolbar.editorcore.selectNode(t.node);
30333                                     toolbar.editorcore.onEditorEvent();   
30334                                                          
30335                                 }
30336                             },
30337                             xns : rooui.menu
30338                         },
30339                        {
30340                             xtype : 'Separator',
30341                             xns : rooui.menu
30342                         },
30343                         {
30344                             xtype : 'Item',
30345                             html: 'Table',
30346                             listeners : {
30347                                 click : function (_self, e)
30348                                 {
30349                                     var t = table();
30350                                     var nn = t.node.nextSibling || t.node.previousSibling;
30351                                     t.node.parentNode.removeChild(t.node);
30352                                     if (nn) { 
30353                                         toolbar.editorcore.selectNode(nn, true);
30354                                     }
30355                                     toolbar.editorcore.onEditorEvent();   
30356                                                          
30357                                 }
30358                             },
30359                             xns : rooui.menu
30360                         }
30361                     ]
30362                 }
30363             }
30364             
30365             // align... << fixme
30366             
30367         ];
30368         
30369     },
30370     
30371     
30372   /**
30373      * create a DomHelper friendly object - for use with
30374      * Roo.DomHelper.markup / overwrite / etc..
30375      * ?? should it be called with option to hide all editing features?
30376      */
30377  /**
30378      * create a DomHelper friendly object - for use with
30379      * Roo.DomHelper.markup / overwrite / etc..
30380      * ?? should it be called with option to hide all editing features?
30381      */
30382     toObject : function()
30383     {
30384         var ret = {
30385             tag : 'td',
30386             contenteditable : 'true', // this stops cell selection from picking the table.
30387             'data-block' : 'Td',
30388             valign : this.valign,
30389             style : {  
30390                 'text-align' :  this.textAlign,
30391                 border : 'solid 1px rgb(0, 0, 0)', // ??? hard coded?
30392                 'border-collapse' : 'collapse',
30393                 padding : '6px', // 8 for desktop / 4 for mobile
30394                 'vertical-align': this.valign
30395             },
30396             html : this.html
30397         };
30398         if (this.width != '') {
30399             ret.width = this.width;
30400             ret.style.width = this.width;
30401         }
30402         
30403         
30404         if (this.colspan > 1) {
30405             ret.colspan = this.colspan ;
30406         } 
30407         if (this.rowspan > 1) {
30408             ret.rowspan = this.rowspan ;
30409         }
30410         
30411            
30412         
30413         return ret;
30414          
30415     },
30416     
30417     readElement : function(node)
30418     {
30419         node  = node ? node : this.node ;
30420         this.width = node.style.width;
30421         this.colspan = Math.max(1,1*node.getAttribute('colspan'));
30422         this.rowspan = Math.max(1,1*node.getAttribute('rowspan'));
30423         this.html = node.innerHTML;
30424         if (node.style.textAlign != '') {
30425             this.textAlign = node.style.textAlign;
30426         }
30427         
30428         
30429     },
30430      
30431     // the default cell object... at present...
30432     emptyCell : function() {
30433         return {
30434             colspan :  1,
30435             rowspan :  1,
30436             textAlign : 'left',
30437             html : "&nbsp;" // is this going to be editable now?
30438         };
30439      
30440     },
30441     
30442     removeNode : function()
30443     {
30444         return this.node.closest('table');
30445          
30446     },
30447     
30448     cellData : false,
30449     
30450     colWidths : false,
30451     
30452     toTableArray  : function()
30453     {
30454         var ret = [];
30455         var tab = this.node.closest('tr').closest('table');
30456         Array.from(tab.rows).forEach(function(r, ri){
30457             ret[ri] = [];
30458         });
30459         var rn = 0;
30460         this.colWidths = [];
30461         var all_auto = true;
30462         Array.from(tab.rows).forEach(function(r, ri){
30463             
30464             var cn = 0;
30465             Array.from(r.cells).forEach(function(ce, ci){
30466                 var c =  {
30467                     cell : ce,
30468                     row : rn,
30469                     col: cn,
30470                     colspan : ce.colSpan,
30471                     rowspan : ce.rowSpan
30472                 };
30473                 if (ce.isEqualNode(this.node)) {
30474                     this.cellData = c;
30475                 }
30476                 // if we have been filled up by a row?
30477                 if (typeof(ret[rn][cn]) != 'undefined') {
30478                     while(typeof(ret[rn][cn]) != 'undefined') {
30479                         cn++;
30480                     }
30481                     c.col = cn;
30482                 }
30483                 
30484                 if (typeof(this.colWidths[cn]) == 'undefined' && c.colspan < 2) {
30485                     this.colWidths[cn] =   ce.style.width;
30486                     if (this.colWidths[cn] != '') {
30487                         all_auto = false;
30488                     }
30489                 }
30490                 
30491                 
30492                 if (c.colspan < 2 && c.rowspan < 2 ) {
30493                     ret[rn][cn] = c;
30494                     cn++;
30495                     return;
30496                 }
30497                 for(var j = 0; j < c.rowspan; j++) {
30498                     if (typeof(ret[rn+j]) == 'undefined') {
30499                         continue; // we have a problem..
30500                     }
30501                     ret[rn+j][cn] = c;
30502                     for(var i = 0; i < c.colspan; i++) {
30503                         ret[rn+j][cn+i] = c;
30504                     }
30505                 }
30506                 
30507                 cn += c.colspan;
30508             }, this);
30509             rn++;
30510         }, this);
30511         
30512         // initalize widths.?
30513         // either all widths or no widths..
30514         if (all_auto) {
30515             this.colWidths[0] = false; // no widths flag.
30516         }
30517         
30518         
30519         return ret;
30520         
30521     },
30522     
30523     
30524     
30525     
30526     mergeRight: function()
30527     {
30528          
30529         // get the contents of the next cell along..
30530         var tr = this.node.closest('tr');
30531         var i = Array.prototype.indexOf.call(tr.childNodes, this.node);
30532         if (i >= tr.childNodes.length - 1) {
30533             return; // no cells on right to merge with.
30534         }
30535         var table = this.toTableArray();
30536         
30537         if (typeof(table[this.cellData.row][this.cellData.col+this.cellData.colspan]) == 'undefined') {
30538             return; // nothing right?
30539         }
30540         var rc = table[this.cellData.row][this.cellData.col+this.cellData.colspan];
30541         // right cell - must be same rowspan and on the same row.
30542         if (rc.rowspan != this.cellData.rowspan || rc.row != this.cellData.row) {
30543             return; // right hand side is not same rowspan.
30544         }
30545         
30546         
30547         
30548         this.node.innerHTML += ' ' + rc.cell.innerHTML;
30549         tr.removeChild(rc.cell);
30550         this.colspan += rc.colspan;
30551         this.node.setAttribute('colspan', this.colspan);
30552
30553         var table = this.toTableArray();
30554         this.normalizeWidths(table);
30555         this.updateWidths(table);
30556     },
30557     
30558     
30559     mergeBelow : function()
30560     {
30561         var table = this.toTableArray();
30562         if (typeof(table[this.cellData.row+this.cellData.rowspan]) == 'undefined') {
30563             return; // no row below
30564         }
30565         if (typeof(table[this.cellData.row+this.cellData.rowspan][this.cellData.col]) == 'undefined') {
30566             return; // nothing right?
30567         }
30568         var rc = table[this.cellData.row+this.cellData.rowspan][this.cellData.col];
30569         
30570         if (rc.colspan != this.cellData.colspan || rc.col != this.cellData.col) {
30571             return; // right hand side is not same rowspan.
30572         }
30573         this.node.innerHTML =  this.node.innerHTML + rc.cell.innerHTML ;
30574         rc.cell.parentNode.removeChild(rc.cell);
30575         this.rowspan += rc.rowspan;
30576         this.node.setAttribute('rowspan', this.rowspan);
30577     },
30578     
30579     split: function()
30580     {
30581         if (this.node.rowSpan < 2 && this.node.colSpan < 2) {
30582             return;
30583         }
30584         var table = this.toTableArray();
30585         var cd = this.cellData;
30586         this.rowspan = 1;
30587         this.colspan = 1;
30588         
30589         for(var r = cd.row; r < cd.row + cd.rowspan; r++) {
30590              
30591             
30592             for(var c = cd.col; c < cd.col + cd.colspan; c++) {
30593                 if (r == cd.row && c == cd.col) {
30594                     this.node.removeAttribute('rowspan');
30595                     this.node.removeAttribute('colspan');
30596                 }
30597                  
30598                 var ntd = this.node.cloneNode(); // which col/row should be 0..
30599                 ntd.removeAttribute('id'); 
30600                 ntd.style.width  = this.colWidths[c];
30601                 ntd.innerHTML = '';
30602                 table[r][c] = { cell : ntd, col : c, row: r , colspan : 1 , rowspan : 1   };
30603             }
30604             
30605         }
30606         this.redrawAllCells(table);
30607         
30608     },
30609     
30610     
30611     
30612     redrawAllCells: function(table)
30613     {
30614         
30615          
30616         var tab = this.node.closest('tr').closest('table');
30617         var ctr = tab.rows[0].parentNode;
30618         Array.from(tab.rows).forEach(function(r, ri){
30619             
30620             Array.from(r.cells).forEach(function(ce, ci){
30621                 ce.parentNode.removeChild(ce);
30622             });
30623             r.parentNode.removeChild(r);
30624         });
30625         for(var r = 0 ; r < table.length; r++) {
30626             var re = tab.rows[r];
30627             
30628             var re = tab.ownerDocument.createElement('tr');
30629             ctr.appendChild(re);
30630             for(var c = 0 ; c < table[r].length; c++) {
30631                 if (table[r][c].cell === false) {
30632                     continue;
30633                 }
30634                 
30635                 re.appendChild(table[r][c].cell);
30636                  
30637                 table[r][c].cell = false;
30638             }
30639         }
30640         
30641     },
30642     updateWidths : function(table)
30643     {
30644         for(var r = 0 ; r < table.length; r++) {
30645            
30646             for(var c = 0 ; c < table[r].length; c++) {
30647                 if (table[r][c].cell === false) {
30648                     continue;
30649                 }
30650                 
30651                 if (this.colWidths[0] != false && table[r][c].colspan < 2) {
30652                     var el = Roo.htmleditor.Block.factory(table[r][c].cell);
30653                     el.width = Math.floor(this.colWidths[c])  +'%';
30654                     el.updateElement(el.node);
30655                 }
30656                 if (this.colWidths[0] != false && table[r][c].colspan > 1) {
30657                     var el = Roo.htmleditor.Block.factory(table[r][c].cell);
30658                     var width = 0;
30659                     for(var i = 0; i < table[r][c].colspan; i ++) {
30660                         width += Math.floor(this.colWidths[c + i]);
30661                     }
30662                     el.width = width  +'%';
30663                     el.updateElement(el.node);
30664                 }
30665                 table[r][c].cell = false; // done
30666             }
30667         }
30668     },
30669     normalizeWidths : function(table)
30670     {
30671         if (this.colWidths[0] === false) {
30672             var nw = 100.0 / this.colWidths.length;
30673             this.colWidths.forEach(function(w,i) {
30674                 this.colWidths[i] = nw;
30675             },this);
30676             return;
30677         }
30678     
30679         var t = 0, missing = [];
30680         
30681         this.colWidths.forEach(function(w,i) {
30682             //if you mix % and
30683             this.colWidths[i] = this.colWidths[i] == '' ? 0 : (this.colWidths[i]+'').replace(/[^0-9]+/g,'')*1;
30684             var add =  this.colWidths[i];
30685             if (add > 0) {
30686                 t+=add;
30687                 return;
30688             }
30689             missing.push(i);
30690             
30691             
30692         },this);
30693         var nc = this.colWidths.length;
30694         if (missing.length) {
30695             var mult = (nc - missing.length) / (1.0 * nc);
30696             var t = mult * t;
30697             var ew = (100 -t) / (1.0 * missing.length);
30698             this.colWidths.forEach(function(w,i) {
30699                 if (w > 0) {
30700                     this.colWidths[i] = w * mult;
30701                     return;
30702                 }
30703                 
30704                 this.colWidths[i] = ew;
30705             }, this);
30706             // have to make up numbers..
30707              
30708         }
30709         // now we should have all the widths..
30710         
30711     
30712     },
30713     
30714     shrinkColumn : function()
30715     {
30716         var table = this.toTableArray();
30717         this.normalizeWidths(table);
30718         var col = this.cellData.col;
30719         var nw = this.colWidths[col] * 0.8;
30720         if (nw < 5) {
30721             return;
30722         }
30723         var otherAdd = (this.colWidths[col]  * 0.2) / (this.colWidths.length -1);
30724         this.colWidths.forEach(function(w,i) {
30725             if (i == col) {
30726                  this.colWidths[i] = nw;
30727                 return;
30728             }
30729             this.colWidths[i] += otherAdd
30730         }, this);
30731         this.updateWidths(table);
30732          
30733     },
30734     growColumn : function()
30735     {
30736         var table = this.toTableArray();
30737         this.normalizeWidths(table);
30738         var col = this.cellData.col;
30739         var nw = this.colWidths[col] * 1.2;
30740         if (nw > 90) {
30741             return;
30742         }
30743         var otherSub = (this.colWidths[col]  * 0.2) / (this.colWidths.length -1);
30744         this.colWidths.forEach(function(w,i) {
30745             if (i == col) {
30746                 this.colWidths[i] = nw;
30747                 return;
30748             }
30749             this.colWidths[i] -= otherSub
30750         }, this);
30751         this.updateWidths(table);
30752          
30753     },
30754     deleteRow : function()
30755     {
30756         // delete this rows 'tr'
30757         // if any of the cells in this row have a rowspan > 1 && row!= this row..
30758         // then reduce the rowspan.
30759         var table = this.toTableArray();
30760         // this.cellData.row;
30761         for (var i =0;i< table[this.cellData.row].length ; i++) {
30762             var c = table[this.cellData.row][i];
30763             if (c.row != this.cellData.row) {
30764                 
30765                 c.rowspan--;
30766                 c.cell.setAttribute('rowspan', c.rowspan);
30767                 continue;
30768             }
30769             if (c.rowspan > 1) {
30770                 c.rowspan--;
30771                 c.cell.setAttribute('rowspan', c.rowspan);
30772             }
30773         }
30774         table.splice(this.cellData.row,1);
30775         this.redrawAllCells(table);
30776         
30777     },
30778     deleteColumn : function()
30779     {
30780         var table = this.toTableArray();
30781         
30782         for (var i =0;i< table.length ; i++) {
30783             var c = table[i][this.cellData.col];
30784             if (c.col != this.cellData.col) {
30785                 table[i][this.cellData.col].colspan--;
30786             } else if (c.colspan > 1) {
30787                 c.colspan--;
30788                 c.cell.setAttribute('colspan', c.colspan);
30789             }
30790             table[i].splice(this.cellData.col,1);
30791         }
30792         
30793         this.redrawAllCells(table);
30794     }
30795     
30796     
30797     
30798     
30799 })
30800
30801 //<script type="text/javascript">
30802
30803 /*
30804  * Based  Ext JS Library 1.1.1
30805  * Copyright(c) 2006-2007, Ext JS, LLC.
30806  * LGPL
30807  *
30808  */
30809  
30810 /**
30811  * @class Roo.HtmlEditorCore
30812  * @extends Roo.Component
30813  * Provides a the editing component for the HTML editors in Roo. (bootstrap and Roo.form)
30814  *
30815  * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
30816  */
30817
30818 Roo.HtmlEditorCore = function(config){
30819     
30820     
30821     Roo.HtmlEditorCore.superclass.constructor.call(this, config);
30822     
30823     
30824     this.addEvents({
30825         /**
30826          * @event initialize
30827          * Fires when the editor is fully initialized (including the iframe)
30828          * @param {Roo.HtmlEditorCore} this
30829          */
30830         initialize: true,
30831         /**
30832          * @event activate
30833          * Fires when the editor is first receives the focus. Any insertion must wait
30834          * until after this event.
30835          * @param {Roo.HtmlEditorCore} this
30836          */
30837         activate: true,
30838          /**
30839          * @event beforesync
30840          * Fires before the textarea is updated with content from the editor iframe. Return false
30841          * to cancel the sync.
30842          * @param {Roo.HtmlEditorCore} this
30843          * @param {String} html
30844          */
30845         beforesync: true,
30846          /**
30847          * @event beforepush
30848          * Fires before the iframe editor is updated with content from the textarea. Return false
30849          * to cancel the push.
30850          * @param {Roo.HtmlEditorCore} this
30851          * @param {String} html
30852          */
30853         beforepush: true,
30854          /**
30855          * @event sync
30856          * Fires when the textarea is updated with content from the editor iframe.
30857          * @param {Roo.HtmlEditorCore} this
30858          * @param {String} html
30859          */
30860         sync: true,
30861          /**
30862          * @event push
30863          * Fires when the iframe editor is updated with content from the textarea.
30864          * @param {Roo.HtmlEditorCore} this
30865          * @param {String} html
30866          */
30867         push: true,
30868         
30869         /**
30870          * @event editorevent
30871          * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
30872          * @param {Roo.HtmlEditorCore} this
30873          */
30874         editorevent: true 
30875         
30876         
30877     });
30878     
30879     // at this point this.owner is set, so we can start working out the whitelisted / blacklisted elements
30880     
30881     // defaults : white / black...
30882     this.applyBlacklists();
30883     
30884     
30885     
30886 };
30887
30888
30889 Roo.extend(Roo.HtmlEditorCore, Roo.Component,  {
30890
30891
30892      /**
30893      * @cfg {Roo.form.HtmlEditor|Roo.bootstrap.HtmlEditor} the owner field 
30894      */
30895     
30896     owner : false,
30897     
30898      /**
30899      * @cfg {String} css styling for resizing. (used on bootstrap only)
30900      */
30901     resize : false,
30902      /**
30903      * @cfg {Number} height (in pixels)
30904      */   
30905     height: 300,
30906    /**
30907      * @cfg {Number} width (in pixels)
30908      */   
30909     width: 500,
30910      /**
30911      * @cfg {boolean} autoClean - default true - loading and saving will remove quite a bit of formating,
30912      *         if you are doing an email editor, this probably needs disabling, it's designed
30913      */
30914     autoClean: true,
30915     
30916     /**
30917      * @cfg {boolean} enableBlocks - default true - if the block editor (table and figure should be enabled)
30918      */
30919     enableBlocks : true,
30920     /**
30921      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
30922      * 
30923      */
30924     stylesheets: false,
30925      /**
30926      * @cfg {String} language default en - language of text (usefull for rtl languages)
30927      * 
30928      */
30929     language: 'en',
30930     
30931     /**
30932      * @cfg {boolean} allowComments - default false - allow comments in HTML source
30933      *          - by default they are stripped - if you are editing email you may need this.
30934      */
30935     allowComments: false,
30936     // id of frame..
30937     frameId: false,
30938     
30939     // private properties
30940     validationEvent : false,
30941     deferHeight: true,
30942     initialized : false,
30943     activated : false,
30944     sourceEditMode : false,
30945     onFocus : Roo.emptyFn,
30946     iframePad:3,
30947     hideMode:'offsets',
30948     
30949     clearUp: true,
30950     
30951     // blacklist + whitelisted elements..
30952     black: false,
30953     white: false,
30954      
30955     bodyCls : '',
30956
30957     
30958     undoManager : false,
30959     /**
30960      * Protected method that will not generally be called directly. It
30961      * is called when the editor initializes the iframe with HTML contents. Override this method if you
30962      * want to change the initialization markup of the iframe (e.g. to add stylesheets).
30963      */
30964     getDocMarkup : function(){
30965         // body styles..
30966         var st = '';
30967         
30968         // inherit styels from page...?? 
30969         if (this.stylesheets === false) {
30970             
30971             Roo.get(document.head).select('style').each(function(node) {
30972                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
30973             });
30974             
30975             Roo.get(document.head).select('link').each(function(node) { 
30976                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
30977             });
30978             
30979         } else if (!this.stylesheets.length) {
30980                 // simple..
30981                 st = '<style type="text/css">' +
30982                     'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
30983                    '</style>';
30984         } else {
30985             for (var i in this.stylesheets) {
30986                 if (typeof(this.stylesheets[i]) != 'string') {
30987                     continue;
30988                 }
30989                 st += '<link rel="stylesheet" href="' + this.stylesheets[i] +'" type="text/css">';
30990             }
30991             
30992         }
30993         
30994         st +=  '<style type="text/css">' +
30995             'IMG { cursor: pointer } ' +
30996         '</style>';
30997         
30998         st += '<meta name="google" content="notranslate">';
30999         
31000         var cls = 'notranslate roo-htmleditor-body';
31001         
31002         if(this.bodyCls.length){
31003             cls += ' ' + this.bodyCls;
31004         }
31005         
31006         return '<html  class="notranslate" translate="no"><head>' + st  +
31007             //<style type="text/css">' +
31008             //'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
31009             //'</style>' +
31010             ' </head><body contenteditable="true" data-enable-grammerly="true" class="' +  cls + '"></body></html>';
31011     },
31012
31013     // private
31014     onRender : function(ct, position)
31015     {
31016         var _t = this;
31017         //Roo.HtmlEditorCore.superclass.onRender.call(this, ct, position);
31018         this.el = this.owner.inputEl ? this.owner.inputEl() : this.owner.el;
31019         
31020         
31021         this.el.dom.style.border = '0 none';
31022         this.el.dom.setAttribute('tabIndex', -1);
31023         this.el.addClass('x-hidden hide');
31024         
31025         
31026         
31027         if(Roo.isIE){ // fix IE 1px bogus margin
31028             this.el.applyStyles('margin-top:-1px;margin-bottom:-1px;')
31029         }
31030        
31031         
31032         this.frameId = Roo.id();
31033         
31034         var ifcfg = {
31035             tag: 'iframe',
31036             cls: 'form-control', // bootstrap..
31037             id: this.frameId,
31038             name: this.frameId,
31039             frameBorder : 'no',
31040             'src' : Roo.SSL_SECURE_URL ? Roo.SSL_SECURE_URL  :  "javascript:false"
31041         };
31042         if (this.resize) {
31043             ifcfg.style = { resize : this.resize };
31044         }
31045         
31046         var iframe = this.owner.wrap.createChild(ifcfg, this.el); 
31047         
31048         
31049         this.iframe = iframe.dom;
31050
31051         this.assignDocWin();
31052         
31053         this.doc.designMode = 'on';
31054        
31055         this.doc.open();
31056         this.doc.write(this.getDocMarkup());
31057         this.doc.close();
31058
31059         
31060         var task = { // must defer to wait for browser to be ready
31061             run : function(){
31062                 //console.log("run task?" + this.doc.readyState);
31063                 this.assignDocWin();
31064                 if(this.doc.body || this.doc.readyState == 'complete'){
31065                     try {
31066                         this.doc.designMode="on";
31067                         
31068                     } catch (e) {
31069                         return;
31070                     }
31071                     Roo.TaskMgr.stop(task);
31072                     this.initEditor.defer(10, this);
31073                 }
31074             },
31075             interval : 10,
31076             duration: 10000,
31077             scope: this
31078         };
31079         Roo.TaskMgr.start(task);
31080
31081     },
31082
31083     // private
31084     onResize : function(w, h)
31085     {
31086          Roo.log('resize: ' +w + ',' + h );
31087         //Roo.HtmlEditorCore.superclass.onResize.apply(this, arguments);
31088         if(!this.iframe){
31089             return;
31090         }
31091         if(typeof w == 'number'){
31092             
31093             this.iframe.style.width = w + 'px';
31094         }
31095         if(typeof h == 'number'){
31096             
31097             this.iframe.style.height = h + 'px';
31098             if(this.doc){
31099                 (this.doc.body || this.doc.documentElement).style.height = (h - (this.iframePad*2)) + 'px';
31100             }
31101         }
31102         
31103     },
31104
31105     /**
31106      * Toggles the editor between standard and source edit mode.
31107      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
31108      */
31109     toggleSourceEdit : function(sourceEditMode){
31110         
31111         this.sourceEditMode = sourceEditMode === true;
31112         
31113         if(this.sourceEditMode){
31114  
31115             Roo.get(this.iframe).addClass(['x-hidden','hide', 'd-none']);     //FIXME - what's the BS styles for these
31116             
31117         }else{
31118             Roo.get(this.iframe).removeClass(['x-hidden','hide', 'd-none']);
31119             //this.iframe.className = '';
31120             this.deferFocus();
31121         }
31122         //this.setSize(this.owner.wrap.getSize());
31123         //this.fireEvent('editmodechange', this, this.sourceEditMode);
31124     },
31125
31126     
31127   
31128
31129     /**
31130      * Protected method that will not generally be called directly. If you need/want
31131      * custom HTML cleanup, this is the method you should override.
31132      * @param {String} html The HTML to be cleaned
31133      * return {String} The cleaned HTML
31134      */
31135     cleanHtml : function(html)
31136     {
31137         html = String(html);
31138         if(html.length > 5){
31139             if(Roo.isSafari){ // strip safari nonsense
31140                 html = html.replace(/\sclass="(?:Apple-style-span|khtml-block-placeholder)"/gi, '');
31141             }
31142         }
31143         if(html == '&nbsp;'){
31144             html = '';
31145         }
31146         return html;
31147     },
31148
31149     /**
31150      * HTML Editor -> Textarea
31151      * Protected method that will not generally be called directly. Syncs the contents
31152      * of the editor iframe with the textarea.
31153      */
31154     syncValue : function()
31155     {
31156         //Roo.log("HtmlEditorCore:syncValue (EDITOR->TEXT)");
31157         if(this.initialized){
31158             
31159             if (this.undoManager) {
31160                 this.undoManager.addEvent();
31161             }
31162
31163             
31164             var bd = (this.doc.body || this.doc.documentElement);
31165            
31166             
31167             var sel = this.win.getSelection();
31168             
31169             var div = document.createElement('div');
31170             div.innerHTML = bd.innerHTML;
31171             var gtx = div.getElementsByClassName('gtx-trans-icon'); // google translate - really annoying and difficult to get rid of.
31172             if (gtx.length > 0) {
31173                 var rm = gtx.item(0).parentNode;
31174                 rm.parentNode.removeChild(rm);
31175             }
31176             
31177            
31178             if (this.enableBlocks) {
31179                 new Roo.htmleditor.FilterBlock({ node : div });
31180             }
31181             
31182             var html = div.innerHTML;
31183             
31184             //?? tidy?
31185             if (this.autoClean) {
31186                 
31187                 new Roo.htmleditor.FilterAttributes({
31188                     node : div,
31189                     attrib_white : [
31190                             'href',
31191                             'src',
31192                             'name',
31193                             'align',
31194                             'colspan',
31195                             'rowspan',
31196                             'data-display',
31197                             'data-width',
31198                             'data-caption',
31199                             'start' ,
31200                             'style',
31201                             // youtube embed.
31202                             'class',
31203                             'allowfullscreen',
31204                             'frameborder',
31205                             'width',
31206                             'height',
31207                             'alt'
31208                             ],
31209                     attrib_clean : ['href', 'src' ] 
31210                 });
31211                 
31212                 var tidy = new Roo.htmleditor.TidySerializer({
31213                     inner:  true
31214                 });
31215                 html  = tidy.serialize(div);
31216                 
31217             }
31218             
31219             
31220             if(Roo.isSafari){
31221                 var bs = bd.getAttribute('style'); // Safari puts text-align styles on the body element!
31222                 var m = bs ? bs.match(/text-align:(.*?);/i) : false;
31223                 if(m && m[1]){
31224                     html = '<div style="'+m[0]+'">' + html + '</div>';
31225                 }
31226             }
31227             html = this.cleanHtml(html);
31228             // fix up the special chars.. normaly like back quotes in word...
31229             // however we do not want to do this with chinese..
31230             html = html.replace(/[\uD800-\uDBFF][\uDC00-\uDFFF]|[\u0080-\uFFFF]/g, function(match) {
31231                 
31232                 var cc = match.charCodeAt();
31233
31234                 // Get the character value, handling surrogate pairs
31235                 if (match.length == 2) {
31236                     // It's a surrogate pair, calculate the Unicode code point
31237                     var high = match.charCodeAt(0) - 0xD800;
31238                     var low  = match.charCodeAt(1) - 0xDC00;
31239                     cc = (high * 0x400) + low + 0x10000;
31240                 }  else if (
31241                     (cc >= 0x4E00 && cc < 0xA000 ) ||
31242                     (cc >= 0x3400 && cc < 0x4E00 ) ||
31243                     (cc >= 0xf900 && cc < 0xfb00 )
31244                 ) {
31245                         return match;
31246                 }  
31247          
31248                 // No, use a numeric entity. Here we brazenly (and possibly mistakenly)
31249                 return "&#" + cc + ";";
31250                 
31251                 
31252             });
31253             
31254             
31255              
31256             if(this.owner.fireEvent('beforesync', this, html) !== false){
31257                 this.el.dom.value = html;
31258                 this.owner.fireEvent('sync', this, html);
31259             }
31260         }
31261     },
31262
31263     /**
31264      * TEXTAREA -> EDITABLE
31265      * Protected method that will not generally be called directly. Pushes the value of the textarea
31266      * into the iframe editor.
31267      */
31268     pushValue : function()
31269     {
31270         //Roo.log("HtmlEditorCore:pushValue (TEXT->EDITOR)");
31271         if(this.initialized){
31272             var v = this.el.dom.value.trim();
31273             
31274             
31275             if(this.owner.fireEvent('beforepush', this, v) !== false){
31276                 var d = (this.doc.body || this.doc.documentElement);
31277                 d.innerHTML = v;
31278                  
31279                 this.el.dom.value = d.innerHTML;
31280                 this.owner.fireEvent('push', this, v);
31281             }
31282             if (this.autoClean) {
31283                 new Roo.htmleditor.FilterParagraph({node : this.doc.body}); // paragraphs
31284                 new Roo.htmleditor.FilterSpan({node : this.doc.body}); // empty spans
31285             }
31286             if (this.enableBlocks) {
31287                 Roo.htmleditor.Block.initAll(this.doc.body);
31288             }
31289             
31290             this.updateLanguage();
31291             
31292             var lc = this.doc.body.lastChild;
31293             if (lc && lc.nodeType == 1 && lc.getAttribute("contenteditable") == "false") {
31294                 // add an extra line at the end.
31295                 this.doc.body.appendChild(this.doc.createElement('br'));
31296             }
31297             
31298             
31299         }
31300     },
31301
31302     // private
31303     deferFocus : function(){
31304         this.focus.defer(10, this);
31305     },
31306
31307     // doc'ed in Field
31308     focus : function(){
31309         if(this.win && !this.sourceEditMode){
31310             this.win.focus();
31311         }else{
31312             this.el.focus();
31313         }
31314     },
31315     
31316     assignDocWin: function()
31317     {
31318         var iframe = this.iframe;
31319         
31320          if(Roo.isIE){
31321             this.doc = iframe.contentWindow.document;
31322             this.win = iframe.contentWindow;
31323         } else {
31324 //            if (!Roo.get(this.frameId)) {
31325 //                return;
31326 //            }
31327 //            this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
31328 //            this.win = Roo.get(this.frameId).dom.contentWindow;
31329             
31330             if (!Roo.get(this.frameId) && !iframe.contentDocument) {
31331                 return;
31332             }
31333             
31334             this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
31335             this.win = (iframe.contentWindow || Roo.get(this.frameId).dom.contentWindow);
31336         }
31337     },
31338     
31339     // private
31340     initEditor : function(){
31341         //console.log("INIT EDITOR");
31342         this.assignDocWin();
31343         
31344         
31345         
31346         this.doc.designMode="on";
31347         this.doc.open();
31348         this.doc.write(this.getDocMarkup());
31349         this.doc.close();
31350         
31351         var dbody = (this.doc.body || this.doc.documentElement);
31352         //var ss = this.el.getStyles('font-size', 'font-family', 'background-image', 'background-repeat');
31353         // this copies styles from the containing element into thsi one..
31354         // not sure why we need all of this..
31355         //var ss = this.el.getStyles('font-size', 'background-image', 'background-repeat');
31356         
31357         //var ss = this.el.getStyles( 'background-image', 'background-repeat');
31358         //ss['background-attachment'] = 'fixed'; // w3c
31359         dbody.bgProperties = 'fixed'; // ie
31360         dbody.setAttribute("translate", "no");
31361         
31362         //Roo.DomHelper.applyStyles(dbody, ss);
31363         Roo.EventManager.on(this.doc, {
31364              
31365             'mouseup': this.onEditorEvent,
31366             'dblclick': this.onEditorEvent,
31367             'click': this.onEditorEvent,
31368             'keyup': this.onEditorEvent,
31369             
31370             buffer:100,
31371             scope: this
31372         });
31373         Roo.EventManager.on(this.doc, {
31374             'paste': this.onPasteEvent,
31375             scope : this
31376         });
31377         if(Roo.isGecko){
31378             Roo.EventManager.on(this.doc, 'keypress', this.mozKeyPress, this);
31379         }
31380         //??? needed???
31381         if(Roo.isIE || Roo.isSafari || Roo.isOpera){
31382             Roo.EventManager.on(this.doc, 'keydown', this.fixKeys, this);
31383         }
31384         this.initialized = true;
31385
31386         
31387         // initialize special key events - enter
31388         new Roo.htmleditor.KeyEnter({core : this});
31389         
31390          
31391         
31392         this.owner.fireEvent('initialize', this);
31393         this.pushValue();
31394     },
31395     // this is to prevent a href clicks resulting in a redirect?
31396    
31397     onPasteEvent : function(e,v)
31398     {
31399         // I think we better assume paste is going to be a dirty load of rubish from word..
31400         
31401         // even pasting into a 'email version' of this widget will have to clean up that mess.
31402         var cd = (e.browserEvent.clipboardData || window.clipboardData);
31403         
31404         // check what type of paste - if it's an image, then handle it differently.
31405         if (cd.files && cd.files.length > 0 && cd.types.indexOf('text/html') < 0) {
31406             // pasting images? 
31407             var urlAPI = (window.createObjectURL && window) || 
31408                 (window.URL && URL.revokeObjectURL && URL) || 
31409                 (window.webkitURL && webkitURL);
31410             
31411             var r = new FileReader();
31412             var t = this;
31413             r.addEventListener('load',function()
31414             {
31415                 
31416                 var d = (new DOMParser().parseFromString('<img src="' + r.result+ '">', 'text/html')).body;
31417                 // is insert asycn?
31418                 if (t.enableBlocks) {
31419                     
31420                     Array.from(d.getElementsByTagName('img')).forEach(function(img) {
31421                         if (img.closest('figure')) { // assume!! that it's aready
31422                             return;
31423                         }
31424                         var fig  = new Roo.htmleditor.BlockFigure({
31425                             image_src  : img.src
31426                         });
31427                         fig.updateElement(img); // replace it..
31428                         
31429                     });
31430                 }
31431                 t.insertAtCursor(d.innerHTML.replace(/&nbsp;/g,' '));
31432                 t.owner.fireEvent('paste', this);
31433             });
31434             r.readAsDataURL(cd.files[0]);
31435             
31436             e.preventDefault();
31437             
31438             return false;
31439         }
31440         if (cd.types.indexOf('text/html') < 0 ) {
31441             return false;
31442         }
31443         var images = [];
31444         var html = cd.getData('text/html'); // clipboard event
31445         if (cd.types.indexOf('text/rtf') > -1) {
31446             var parser = new Roo.rtf.Parser(cd.getData('text/rtf'));
31447             images = parser.doc ? parser.doc.getElementsByType('pict') : [];
31448         }
31449         //Roo.log(images);
31450         //Roo.log(imgs);
31451         // fixme..
31452         images = images.filter(function(g) { return !g.path.match(/^rtf\/(head|pgdsctbl|listtable|footerf)/); }) // ignore headers/footers etc.
31453                        .map(function(g) { return g.toDataURL(); })
31454                        .filter(function(g) { return g != 'about:blank'; });
31455         
31456         //Roo.log(html);
31457         html = this.cleanWordChars(html);
31458         
31459         var d = (new DOMParser().parseFromString(html, 'text/html')).body;
31460         
31461         
31462         var sn = this.getParentElement();
31463         // check if d contains a table, and prevent nesting??
31464         //Roo.log(d.getElementsByTagName('table'));
31465         //Roo.log(sn);
31466         //Roo.log(sn.closest('table'));
31467         if (d.getElementsByTagName('table').length && sn && sn.closest('table')) {
31468             e.preventDefault();
31469             this.insertAtCursor("You can not nest tables");
31470             //Roo.log("prevent?"); // fixme - 
31471             return false;
31472         }
31473         
31474         
31475         
31476         if (images.length > 0) {
31477             // replace all v:imagedata - with img.
31478             var ar = Array.from(d.getElementsByTagName('v:imagedata'));
31479             Roo.each(ar, function(node) {
31480                 node.parentNode.insertBefore(d.ownerDocument.createElement('img'), node );
31481                 node.parentNode.removeChild(node);
31482             });
31483             
31484             
31485             Roo.each(d.getElementsByTagName('img'), function(img, i) {
31486                 img.setAttribute('src', images[i]);
31487             });
31488         }
31489         if (this.autoClean) {
31490             new Roo.htmleditor.FilterWord({ node : d });
31491             
31492             new Roo.htmleditor.FilterStyleToTag({ node : d });
31493             new Roo.htmleditor.FilterAttributes({
31494                 node : d,
31495                 attrib_white : ['href', 'src', 'name', 'align', 'colspan', 'rowspan', 'data-display', 'data-width', 'start'],
31496                 attrib_clean : ['href', 'src' ] 
31497             });
31498             new Roo.htmleditor.FilterBlack({ node : d, tag : this.black});
31499             // should be fonts..
31500             new Roo.htmleditor.FilterKeepChildren({node : d, tag : [ 'FONT', ':' ]} );
31501             new Roo.htmleditor.FilterParagraph({ node : d });
31502             new Roo.htmleditor.FilterSpan({ node : d });
31503             new Roo.htmleditor.FilterLongBr({ node : d });
31504             new Roo.htmleditor.FilterComment({ node : d });
31505             
31506             
31507         }
31508         if (this.enableBlocks) {
31509                 
31510             Array.from(d.getElementsByTagName('img')).forEach(function(img) {
31511                 if (img.closest('figure')) { // assume!! that it's aready
31512                     return;
31513                 }
31514                 var fig  = new Roo.htmleditor.BlockFigure({
31515                     image_src  : img.src
31516                 });
31517                 fig.updateElement(img); // replace it..
31518                 
31519             });
31520         }
31521         
31522         
31523         this.insertAtCursor(d.innerHTML.replace(/&nbsp;/g,' '));
31524         if (this.enableBlocks) {
31525             Roo.htmleditor.Block.initAll(this.doc.body);
31526         }
31527          
31528         
31529         e.preventDefault();
31530         this.owner.fireEvent('paste', this);
31531         return false;
31532         // default behaveiour should be our local cleanup paste? (optional?)
31533         // for simple editor - we want to hammer the paste and get rid of everything... - so over-rideable..
31534         //this.owner.fireEvent('paste', e, v);
31535     },
31536     // private
31537     onDestroy : function(){
31538         
31539         
31540         
31541         if(this.rendered){
31542             
31543             //for (var i =0; i < this.toolbars.length;i++) {
31544             //    // fixme - ask toolbars for heights?
31545             //    this.toolbars[i].onDestroy();
31546            // }
31547             
31548             //this.wrap.dom.innerHTML = '';
31549             //this.wrap.remove();
31550         }
31551     },
31552
31553     // private
31554     onFirstFocus : function(){
31555         
31556         this.assignDocWin();
31557         this.undoManager = new Roo.lib.UndoManager(100,(this.doc.body || this.doc.documentElement));
31558         
31559         this.activated = true;
31560          
31561     
31562         if(Roo.isGecko){ // prevent silly gecko errors
31563             this.win.focus();
31564             var s = this.win.getSelection();
31565             if(!s.focusNode || s.focusNode.nodeType != 3){
31566                 var r = s.getRangeAt(0);
31567                 r.selectNodeContents((this.doc.body || this.doc.documentElement));
31568                 r.collapse(true);
31569                 this.deferFocus();
31570             }
31571             try{
31572                 this.execCmd('useCSS', true);
31573                 this.execCmd('styleWithCSS', false);
31574             }catch(e){}
31575         }
31576         this.owner.fireEvent('activate', this);
31577     },
31578
31579     // private
31580     adjustFont: function(btn){
31581         var adjust = btn.cmd == 'increasefontsize' ? 1 : -1;
31582         //if(Roo.isSafari){ // safari
31583         //    adjust *= 2;
31584        // }
31585         var v = parseInt(this.doc.queryCommandValue('FontSize')|| 3, 10);
31586         if(Roo.isSafari){ // safari
31587             var sm = { 10 : 1, 13: 2, 16:3, 18:4, 24: 5, 32:6, 48: 7 };
31588             v =  (v < 10) ? 10 : v;
31589             v =  (v > 48) ? 48 : v;
31590             v = typeof(sm[v]) == 'undefined' ? 1 : sm[v];
31591             
31592         }
31593         
31594         
31595         v = Math.max(1, v+adjust);
31596         
31597         this.execCmd('FontSize', v  );
31598     },
31599
31600     onEditorEvent : function(e)
31601     {
31602          
31603         
31604         if (e && (e.ctrlKey || e.metaKey) && e.keyCode === 90) {
31605             return; // we do not handle this.. (undo manager does..)
31606         }
31607         // clicking a 'block'?
31608         
31609         // in theory this detects if the last element is not a br, then we try and do that.
31610         // its so clicking in space at bottom triggers adding a br and moving the cursor.
31611         if (e &&
31612             e.target.nodeName == 'BODY' &&
31613             e.type == "mouseup" &&
31614             this.doc.body.lastChild
31615            ) {
31616             var lc = this.doc.body.lastChild;
31617             // gtx-trans is google translate plugin adding crap.
31618             while ((lc.nodeType == 3 && lc.nodeValue == '') || lc.id == 'gtx-trans') {
31619                 lc = lc.previousSibling;
31620             }
31621             if (lc.nodeType == 1 && lc.nodeName != 'BR') {
31622             // if last element is <BR> - then dont do anything.
31623             
31624                 var ns = this.doc.createElement('br');
31625                 this.doc.body.appendChild(ns);
31626                 range = this.doc.createRange();
31627                 range.setStartAfter(ns);
31628                 range.collapse(true);
31629                 var sel = this.win.getSelection();
31630                 sel.removeAllRanges();
31631                 sel.addRange(range);
31632             }
31633         }
31634         
31635         
31636         
31637         this.fireEditorEvent(e);
31638       //  this.updateToolbar();
31639         this.syncValue(); //we can not sync so often.. sync cleans, so this breaks stuff
31640     },
31641     
31642     fireEditorEvent: function(e)
31643     {
31644         this.owner.fireEvent('editorevent', this, e);
31645     },
31646
31647     insertTag : function(tg)
31648     {
31649         // could be a bit smarter... -> wrap the current selected tRoo..
31650         if (tg.toLowerCase() == 'span' ||
31651             tg.toLowerCase() == 'code' ||
31652             tg.toLowerCase() == 'sup' ||
31653             tg.toLowerCase() == 'sub' 
31654             ) {
31655             
31656             range = this.createRange(this.getSelection());
31657             var wrappingNode = this.doc.createElement(tg.toLowerCase());
31658             wrappingNode.appendChild(range.extractContents());
31659             range.insertNode(wrappingNode);
31660
31661             return;
31662             
31663             
31664             
31665         }
31666         this.execCmd("formatblock",   tg);
31667         this.undoManager.addEvent(); 
31668     },
31669     
31670     insertText : function(txt)
31671     {
31672         
31673         
31674         var range = this.createRange();
31675         range.deleteContents();
31676                //alert(Sender.getAttribute('label'));
31677                
31678         range.insertNode(this.doc.createTextNode(txt));
31679         this.undoManager.addEvent();
31680     } ,
31681     
31682      
31683
31684     /**
31685      * Executes a Midas editor command on the editor document and performs necessary focus and
31686      * toolbar updates. <b>This should only be called after the editor is initialized.</b>
31687      * @param {String} cmd The Midas command
31688      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
31689      */
31690     relayCmd : function(cmd, value)
31691     {
31692         
31693         switch (cmd) {
31694             case 'justifyleft':
31695             case 'justifyright':
31696             case 'justifycenter':
31697                 // if we are in a cell, then we will adjust the
31698                 var n = this.getParentElement();
31699                 var td = n.closest('td');
31700                 if (td) {
31701                     var bl = Roo.htmleditor.Block.factory(td);
31702                     bl.textAlign = cmd.replace('justify','');
31703                     bl.updateElement();
31704                     this.owner.fireEvent('editorevent', this);
31705                     return;
31706                 }
31707                 this.execCmd('styleWithCSS', true); // 
31708                 break;
31709             case 'bold':
31710             case 'italic':
31711             case 'underline':                
31712                 // if there is no selection, then we insert, and set the curson inside it..
31713                 this.execCmd('styleWithCSS', false); 
31714                 break;
31715                 
31716         
31717             default:
31718                 break;
31719         }
31720         
31721         
31722         this.win.focus();
31723         this.execCmd(cmd, value);
31724         this.owner.fireEvent('editorevent', this);
31725         //this.updateToolbar();
31726         this.owner.deferFocus();
31727     },
31728
31729     /**
31730      * Executes a Midas editor command directly on the editor document.
31731      * For visual commands, you should use {@link #relayCmd} instead.
31732      * <b>This should only be called after the editor is initialized.</b>
31733      * @param {String} cmd The Midas command
31734      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
31735      */
31736     execCmd : function(cmd, value){
31737         this.doc.execCommand(cmd, false, value === undefined ? null : value);
31738         this.syncValue();
31739     },
31740  
31741  
31742    
31743     /**
31744      * Inserts the passed text at the current cursor position. Note: the editor must be initialized and activated
31745      * to insert tRoo.
31746      * @param {String} text | dom node.. 
31747      */
31748     insertAtCursor : function(text)
31749     {
31750         
31751         if(!this.activated){
31752             return;
31753         }
31754          
31755         if(Roo.isGecko || Roo.isOpera || Roo.isSafari){
31756             this.win.focus();
31757             
31758             
31759             // from jquery ui (MIT licenced)
31760             var range, node;
31761             var win = this.win;
31762             
31763             if (win.getSelection && win.getSelection().getRangeAt) {
31764                 
31765                 // delete the existing?
31766                 
31767                 this.createRange(this.getSelection()).deleteContents();
31768                 range = win.getSelection().getRangeAt(0);
31769                 node = typeof(text) == 'string' ? range.createContextualFragment(text) : text;
31770                 range.insertNode(node);
31771                 range = range.cloneRange();
31772                 range.collapse(false);
31773                  
31774                 win.getSelection().removeAllRanges();
31775                 win.getSelection().addRange(range);
31776                 
31777                 
31778                 
31779             } else if (win.document.selection && win.document.selection.createRange) {
31780                 // no firefox support
31781                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
31782                 win.document.selection.createRange().pasteHTML(txt);
31783             
31784             } else {
31785                 // no firefox support
31786                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
31787                 this.execCmd('InsertHTML', txt);
31788             } 
31789             this.syncValue();
31790             
31791             this.deferFocus();
31792         }
31793     },
31794  // private
31795     mozKeyPress : function(e){
31796         if(e.ctrlKey){
31797             var c = e.getCharCode(), cmd;
31798           
31799             if(c > 0){
31800                 c = String.fromCharCode(c).toLowerCase();
31801                 switch(c){
31802                     case 'b':
31803                         cmd = 'bold';
31804                         break;
31805                     case 'i':
31806                         cmd = 'italic';
31807                         break;
31808                     
31809                     case 'u':
31810                         cmd = 'underline';
31811                         break;
31812                     
31813                     //case 'v':
31814                       //  this.cleanUpPaste.defer(100, this);
31815                       //  return;
31816                         
31817                 }
31818                 if(cmd){
31819                     
31820                     this.relayCmd(cmd);
31821                     //this.win.focus();
31822                     //this.execCmd(cmd);
31823                     //this.deferFocus();
31824                     e.preventDefault();
31825                 }
31826                 
31827             }
31828         }
31829     },
31830
31831     // private
31832     fixKeys : function(){ // load time branching for fastest keydown performance
31833         
31834         
31835         if(Roo.isIE){
31836             return function(e){
31837                 var k = e.getKey(), r;
31838                 if(k == e.TAB){
31839                     e.stopEvent();
31840                     r = this.doc.selection.createRange();
31841                     if(r){
31842                         r.collapse(true);
31843                         r.pasteHTML('&#160;&#160;&#160;&#160;');
31844                         this.deferFocus();
31845                     }
31846                     return;
31847                 }
31848                 /// this is handled by Roo.htmleditor.KeyEnter
31849                  /*
31850                 if(k == e.ENTER){
31851                     r = this.doc.selection.createRange();
31852                     if(r){
31853                         var target = r.parentElement();
31854                         if(!target || target.tagName.toLowerCase() != 'li'){
31855                             e.stopEvent();
31856                             r.pasteHTML('<br/>');
31857                             r.collapse(false);
31858                             r.select();
31859                         }
31860                     }
31861                 }
31862                 */
31863                 //if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
31864                 //    this.cleanUpPaste.defer(100, this);
31865                 //    return;
31866                 //}
31867                 
31868                 
31869             };
31870         }else if(Roo.isOpera){
31871             return function(e){
31872                 var k = e.getKey();
31873                 if(k == e.TAB){
31874                     e.stopEvent();
31875                     this.win.focus();
31876                     this.execCmd('InsertHTML','&#160;&#160;&#160;&#160;');
31877                     this.deferFocus();
31878                 }
31879                
31880                 //if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
31881                 //    this.cleanUpPaste.defer(100, this);
31882                  //   return;
31883                 //}
31884                 
31885             };
31886         }else if(Roo.isSafari){
31887             return function(e){
31888                 var k = e.getKey();
31889                 
31890                 if(k == e.TAB){
31891                     e.stopEvent();
31892                     this.execCmd('InsertText','\t');
31893                     this.deferFocus();
31894                     return;
31895                 }
31896                  this.mozKeyPress(e);
31897                 
31898                //if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
31899                  //   this.cleanUpPaste.defer(100, this);
31900                  //   return;
31901                // }
31902                 
31903              };
31904         }
31905     }(),
31906     
31907     getAllAncestors: function()
31908     {
31909         var p = this.getSelectedNode();
31910         var a = [];
31911         if (!p) {
31912             a.push(p); // push blank onto stack..
31913             p = this.getParentElement();
31914         }
31915         
31916         
31917         while (p && (p.nodeType == 1) && (p.tagName.toLowerCase() != 'body')) {
31918             a.push(p);
31919             p = p.parentNode;
31920         }
31921         a.push(this.doc.body);
31922         return a;
31923     },
31924     lastSel : false,
31925     lastSelNode : false,
31926     
31927     
31928     getSelection : function() 
31929     {
31930         this.assignDocWin();
31931         return Roo.lib.Selection.wrap(Roo.isIE ? this.doc.selection : this.win.getSelection(), this.doc);
31932     },
31933     /**
31934      * Select a dom node
31935      * @param {DomElement} node the node to select
31936      */
31937     selectNode : function(node, collapse)
31938     {
31939         var nodeRange = node.ownerDocument.createRange();
31940         try {
31941             nodeRange.selectNode(node);
31942         } catch (e) {
31943             nodeRange.selectNodeContents(node);
31944         }
31945         if (collapse === true) {
31946             nodeRange.collapse(true);
31947         }
31948         //
31949         var s = this.win.getSelection();
31950         s.removeAllRanges();
31951         s.addRange(nodeRange);
31952     },
31953     
31954     getSelectedNode: function() 
31955     {
31956         // this may only work on Gecko!!!
31957         
31958         // should we cache this!!!!
31959         
31960          
31961          
31962         var range = this.createRange(this.getSelection()).cloneRange();
31963         
31964         if (Roo.isIE) {
31965             var parent = range.parentElement();
31966             while (true) {
31967                 var testRange = range.duplicate();
31968                 testRange.moveToElementText(parent);
31969                 if (testRange.inRange(range)) {
31970                     break;
31971                 }
31972                 if ((parent.nodeType != 1) || (parent.tagName.toLowerCase() == 'body')) {
31973                     break;
31974                 }
31975                 parent = parent.parentElement;
31976             }
31977             return parent;
31978         }
31979         
31980         // is ancestor a text element.
31981         var ac =  range.commonAncestorContainer;
31982         if (ac.nodeType == 3) {
31983             ac = ac.parentNode;
31984         }
31985         
31986         var ar = ac.childNodes;
31987          
31988         var nodes = [];
31989         var other_nodes = [];
31990         var has_other_nodes = false;
31991         for (var i=0;i<ar.length;i++) {
31992             if ((ar[i].nodeType == 3) && (!ar[i].data.length)) { // empty text ? 
31993                 continue;
31994             }
31995             // fullly contained node.
31996             
31997             if (this.rangeIntersectsNode(range,ar[i]) && this.rangeCompareNode(range,ar[i]) == 3) {
31998                 nodes.push(ar[i]);
31999                 continue;
32000             }
32001             
32002             // probably selected..
32003             if ((ar[i].nodeType == 1) && this.rangeIntersectsNode(range,ar[i]) && (this.rangeCompareNode(range,ar[i]) > 0)) {
32004                 other_nodes.push(ar[i]);
32005                 continue;
32006             }
32007             // outer..
32008             if (!this.rangeIntersectsNode(range,ar[i])|| (this.rangeCompareNode(range,ar[i]) == 0))  {
32009                 continue;
32010             }
32011             
32012             
32013             has_other_nodes = true;
32014         }
32015         if (!nodes.length && other_nodes.length) {
32016             nodes= other_nodes;
32017         }
32018         if (has_other_nodes || !nodes.length || (nodes.length > 1)) {
32019             return false;
32020         }
32021         
32022         return nodes[0];
32023     },
32024     
32025     
32026     createRange: function(sel)
32027     {
32028         // this has strange effects when using with 
32029         // top toolbar - not sure if it's a great idea.
32030         //this.editor.contentWindow.focus();
32031         if (typeof sel != "undefined") {
32032             try {
32033                 return sel.getRangeAt ? sel.getRangeAt(0) : sel.createRange();
32034             } catch(e) {
32035                 return this.doc.createRange();
32036             }
32037         } else {
32038             return this.doc.createRange();
32039         }
32040     },
32041     getParentElement: function()
32042     {
32043         
32044         this.assignDocWin();
32045         var sel = Roo.isIE ? this.doc.selection : this.win.getSelection();
32046         
32047         var range = this.createRange(sel);
32048          
32049         try {
32050             var p = range.commonAncestorContainer;
32051             while (p.nodeType == 3) { // text node
32052                 p = p.parentNode;
32053             }
32054             return p;
32055         } catch (e) {
32056             return null;
32057         }
32058     
32059     },
32060     /***
32061      *
32062      * Range intersection.. the hard stuff...
32063      *  '-1' = before
32064      *  '0' = hits..
32065      *  '1' = after.
32066      *         [ -- selected range --- ]
32067      *   [fail]                        [fail]
32068      *
32069      *    basically..
32070      *      if end is before start or  hits it. fail.
32071      *      if start is after end or hits it fail.
32072      *
32073      *   if either hits (but other is outside. - then it's not 
32074      *   
32075      *    
32076      **/
32077     
32078     
32079     // @see http://www.thismuchiknow.co.uk/?p=64.
32080     rangeIntersectsNode : function(range, node)
32081     {
32082         var nodeRange = node.ownerDocument.createRange();
32083         try {
32084             nodeRange.selectNode(node);
32085         } catch (e) {
32086             nodeRange.selectNodeContents(node);
32087         }
32088     
32089         var rangeStartRange = range.cloneRange();
32090         rangeStartRange.collapse(true);
32091     
32092         var rangeEndRange = range.cloneRange();
32093         rangeEndRange.collapse(false);
32094     
32095         var nodeStartRange = nodeRange.cloneRange();
32096         nodeStartRange.collapse(true);
32097     
32098         var nodeEndRange = nodeRange.cloneRange();
32099         nodeEndRange.collapse(false);
32100     
32101         return rangeStartRange.compareBoundaryPoints(
32102                  Range.START_TO_START, nodeEndRange) == -1 &&
32103                rangeEndRange.compareBoundaryPoints(
32104                  Range.START_TO_START, nodeStartRange) == 1;
32105         
32106          
32107     },
32108     rangeCompareNode : function(range, node)
32109     {
32110         var nodeRange = node.ownerDocument.createRange();
32111         try {
32112             nodeRange.selectNode(node);
32113         } catch (e) {
32114             nodeRange.selectNodeContents(node);
32115         }
32116         
32117         
32118         range.collapse(true);
32119     
32120         nodeRange.collapse(true);
32121      
32122         var ss = range.compareBoundaryPoints( Range.START_TO_START, nodeRange);
32123         var ee = range.compareBoundaryPoints(  Range.END_TO_END, nodeRange);
32124          
32125         //Roo.log(node.tagName + ': ss='+ss +', ee='+ee)
32126         
32127         var nodeIsBefore   =  ss == 1;
32128         var nodeIsAfter    = ee == -1;
32129         
32130         if (nodeIsBefore && nodeIsAfter) {
32131             return 0; // outer
32132         }
32133         if (!nodeIsBefore && nodeIsAfter) {
32134             return 1; //right trailed.
32135         }
32136         
32137         if (nodeIsBefore && !nodeIsAfter) {
32138             return 2;  // left trailed.
32139         }
32140         // fully contined.
32141         return 3;
32142     },
32143  
32144     cleanWordChars : function(input) {// change the chars to hex code
32145         
32146        var swapCodes  = [ 
32147             [    8211, "&#8211;" ], 
32148             [    8212, "&#8212;" ], 
32149             [    8216,  "'" ],  
32150             [    8217, "'" ],  
32151             [    8220, '"' ],  
32152             [    8221, '"' ],  
32153             [    8226, "*" ],  
32154             [    8230, "..." ]
32155         ]; 
32156         var output = input;
32157         Roo.each(swapCodes, function(sw) { 
32158             var swapper = new RegExp("\\u" + sw[0].toString(16), "g"); // hex codes
32159             
32160             output = output.replace(swapper, sw[1]);
32161         });
32162         
32163         return output;
32164     },
32165     
32166      
32167     
32168         
32169     
32170     cleanUpChild : function (node)
32171     {
32172         
32173         new Roo.htmleditor.FilterComment({node : node});
32174         new Roo.htmleditor.FilterAttributes({
32175                 node : node,
32176                 attrib_black : this.ablack,
32177                 attrib_clean : this.aclean,
32178                 style_white : this.cwhite,
32179                 style_black : this.cblack
32180         });
32181         new Roo.htmleditor.FilterBlack({ node : node, tag : this.black});
32182         new Roo.htmleditor.FilterKeepChildren({node : node, tag : this.tag_remove} );
32183          
32184         
32185     },
32186     
32187     /**
32188      * Clean up MS wordisms...
32189      * @deprecated - use filter directly
32190      */
32191     cleanWord : function(node)
32192     {
32193         new Roo.htmleditor.FilterWord({ node : node ? node : this.doc.body });
32194         new Roo.htmleditor.FilterKeepChildren({node : node ? node : this.doc.body, tag : [ 'FONT', ':' ]} );
32195         
32196     },
32197    
32198     
32199     /**
32200
32201      * @deprecated - use filters
32202      */
32203     cleanTableWidths : function(node)
32204     {
32205         new Roo.htmleditor.FilterTableWidth({ node : node ? node : this.doc.body});
32206         
32207  
32208     },
32209     
32210      
32211         
32212     applyBlacklists : function()
32213     {
32214         var w = typeof(this.owner.white) != 'undefined' && this.owner.white ? this.owner.white  : [];
32215         var b = typeof(this.owner.black) != 'undefined' && this.owner.black ? this.owner.black :  [];
32216         
32217         this.aclean = typeof(this.owner.aclean) != 'undefined' && this.owner.aclean ? this.owner.aclean :  Roo.HtmlEditorCore.aclean;
32218         this.ablack = typeof(this.owner.ablack) != 'undefined' && this.owner.ablack ? this.owner.ablack :  Roo.HtmlEditorCore.ablack;
32219         this.tag_remove = typeof(this.owner.tag_remove) != 'undefined' && this.owner.tag_remove ? this.owner.tag_remove :  Roo.HtmlEditorCore.tag_remove;
32220         
32221         this.white = [];
32222         this.black = [];
32223         Roo.each(Roo.HtmlEditorCore.white, function(tag) {
32224             if (b.indexOf(tag) > -1) {
32225                 return;
32226             }
32227             this.white.push(tag);
32228             
32229         }, this);
32230         
32231         Roo.each(w, function(tag) {
32232             if (b.indexOf(tag) > -1) {
32233                 return;
32234             }
32235             if (this.white.indexOf(tag) > -1) {
32236                 return;
32237             }
32238             this.white.push(tag);
32239             
32240         }, this);
32241         
32242         
32243         Roo.each(Roo.HtmlEditorCore.black, function(tag) {
32244             if (w.indexOf(tag) > -1) {
32245                 return;
32246             }
32247             this.black.push(tag);
32248             
32249         }, this);
32250         
32251         Roo.each(b, function(tag) {
32252             if (w.indexOf(tag) > -1) {
32253                 return;
32254             }
32255             if (this.black.indexOf(tag) > -1) {
32256                 return;
32257             }
32258             this.black.push(tag);
32259             
32260         }, this);
32261         
32262         
32263         w = typeof(this.owner.cwhite) != 'undefined' && this.owner.cwhite ? this.owner.cwhite  : [];
32264         b = typeof(this.owner.cblack) != 'undefined' && this.owner.cblack ? this.owner.cblack :  [];
32265         
32266         this.cwhite = [];
32267         this.cblack = [];
32268         Roo.each(Roo.HtmlEditorCore.cwhite, function(tag) {
32269             if (b.indexOf(tag) > -1) {
32270                 return;
32271             }
32272             this.cwhite.push(tag);
32273             
32274         }, this);
32275         
32276         Roo.each(w, function(tag) {
32277             if (b.indexOf(tag) > -1) {
32278                 return;
32279             }
32280             if (this.cwhite.indexOf(tag) > -1) {
32281                 return;
32282             }
32283             this.cwhite.push(tag);
32284             
32285         }, this);
32286         
32287         
32288         Roo.each(Roo.HtmlEditorCore.cblack, function(tag) {
32289             if (w.indexOf(tag) > -1) {
32290                 return;
32291             }
32292             this.cblack.push(tag);
32293             
32294         }, this);
32295         
32296         Roo.each(b, function(tag) {
32297             if (w.indexOf(tag) > -1) {
32298                 return;
32299             }
32300             if (this.cblack.indexOf(tag) > -1) {
32301                 return;
32302             }
32303             this.cblack.push(tag);
32304             
32305         }, this);
32306     },
32307     
32308     setStylesheets : function(stylesheets)
32309     {
32310         if(typeof(stylesheets) == 'string'){
32311             Roo.get(this.iframe.contentDocument.head).createChild({
32312                 tag : 'link',
32313                 rel : 'stylesheet',
32314                 type : 'text/css',
32315                 href : stylesheets
32316             });
32317             
32318             return;
32319         }
32320         var _this = this;
32321      
32322         Roo.each(stylesheets, function(s) {
32323             if(!s.length){
32324                 return;
32325             }
32326             
32327             Roo.get(_this.iframe.contentDocument.head).createChild({
32328                 tag : 'link',
32329                 rel : 'stylesheet',
32330                 type : 'text/css',
32331                 href : s
32332             });
32333         });
32334
32335         
32336     },
32337     
32338     
32339     updateLanguage : function()
32340     {
32341         if (!this.iframe || !this.iframe.contentDocument) {
32342             return;
32343         }
32344         Roo.get(this.iframe.contentDocument.body).attr("lang", this.language);
32345     },
32346     
32347     
32348     removeStylesheets : function()
32349     {
32350         var _this = this;
32351         
32352         Roo.each(Roo.get(_this.iframe.contentDocument.head).select('link[rel=stylesheet]', true).elements, function(s){
32353             s.remove();
32354         });
32355     },
32356     
32357     setStyle : function(style)
32358     {
32359         Roo.get(this.iframe.contentDocument.head).createChild({
32360             tag : 'style',
32361             type : 'text/css',
32362             html : style
32363         });
32364
32365         return;
32366     }
32367     
32368     // hide stuff that is not compatible
32369     /**
32370      * @event blur
32371      * @hide
32372      */
32373     /**
32374      * @event change
32375      * @hide
32376      */
32377     /**
32378      * @event focus
32379      * @hide
32380      */
32381     /**
32382      * @event specialkey
32383      * @hide
32384      */
32385     /**
32386      * @cfg {String} fieldClass @hide
32387      */
32388     /**
32389      * @cfg {String} focusClass @hide
32390      */
32391     /**
32392      * @cfg {String} autoCreate @hide
32393      */
32394     /**
32395      * @cfg {String} inputType @hide
32396      */
32397     /**
32398      * @cfg {String} invalidClass @hide
32399      */
32400     /**
32401      * @cfg {String} invalidText @hide
32402      */
32403     /**
32404      * @cfg {String} msgFx @hide
32405      */
32406     /**
32407      * @cfg {String} validateOnBlur @hide
32408      */
32409 });
32410
32411 Roo.HtmlEditorCore.white = [
32412         'AREA', 'BR', 'IMG', 'INPUT', 'HR', 'WBR',
32413         
32414        'ADDRESS', 'BLOCKQUOTE', 'CENTER', 'DD',      'DIR',       'DIV', 
32415        'DL',      'DT',         'H1',     'H2',      'H3',        'H4', 
32416        'H5',      'H6',         'HR',     'ISINDEX', 'LISTING',   'MARQUEE', 
32417        'MENU',    'MULTICOL',   'OL',     'P',       'PLAINTEXT', 'PRE', 
32418        'TABLE',   'UL',         'XMP', 
32419        
32420        'CAPTION', 'COL', 'COLGROUP', 'TBODY', 'TD', 'TFOOT', 'TH', 
32421       'THEAD',   'TR', 
32422      
32423       'DIR', 'MENU', 'OL', 'UL', 'DL',
32424        
32425       'EMBED',  'OBJECT'
32426 ];
32427
32428
32429 Roo.HtmlEditorCore.black = [
32430     //    'embed',  'object', // enable - backend responsiblity to clean thiese
32431         'APPLET', // 
32432         'BASE',   'BASEFONT', 'BGSOUND', 'BLINK',  'BODY', 
32433         'FRAME',  'FRAMESET', 'HEAD',    'HTML',   'ILAYER', 
32434         'IFRAME', 'LAYER',  'LINK',     'META',    'OBJECT',   
32435         'SCRIPT', 'STYLE' ,'TITLE',  'XML',
32436         //'FONT' // CLEAN LATER..
32437         'COLGROUP', 'COL'   // messy tables.
32438         
32439         
32440 ];
32441 Roo.HtmlEditorCore.clean = [ // ?? needed???
32442      'SCRIPT', 'STYLE', 'TITLE', 'XML'
32443 ];
32444 Roo.HtmlEditorCore.tag_remove = [
32445     'FONT', 'TBODY'  
32446 ];
32447 // attributes..
32448
32449 Roo.HtmlEditorCore.ablack = [
32450     'on'
32451 ];
32452     
32453 Roo.HtmlEditorCore.aclean = [ 
32454     'action', 'background', 'codebase', 'dynsrc', 'href', 'lowsrc' 
32455 ];
32456
32457 // protocols..
32458 Roo.HtmlEditorCore.pwhite= [
32459         'http',  'https',  'mailto'
32460 ];
32461
32462 // white listed style attributes.
32463 Roo.HtmlEditorCore.cwhite= [
32464       //  'text-align', /// default is to allow most things..
32465       
32466          
32467 //        'font-size'//??
32468 ];
32469
32470 // black listed style attributes.
32471 Roo.HtmlEditorCore.cblack= [
32472       //  'font-size' -- this can be set by the project 
32473 ];
32474
32475
32476
32477
32478     /*
32479  * - LGPL
32480  *
32481  * HtmlEditor
32482  * 
32483  */
32484
32485 /**
32486  * @class Roo.bootstrap.form.HtmlEditor
32487  * @extends Roo.bootstrap.form.TextArea
32488  * Bootstrap HtmlEditor class
32489
32490  * @constructor
32491  * Create a new HtmlEditor
32492  * @param {Object} config The config object
32493  */
32494
32495 Roo.bootstrap.form.HtmlEditor = function(config){
32496
32497     this.addEvents({
32498             /**
32499              * @event initialize
32500              * Fires when the editor is fully initialized (including the iframe)
32501              * @param {Roo.bootstrap.form.HtmlEditor} this
32502              */
32503             initialize: true,
32504             /**
32505              * @event activate
32506              * Fires when the editor is first receives the focus. Any insertion must wait
32507              * until after this event.
32508              * @param {Roo.bootstrap.form.HtmlEditor} this
32509              */
32510             activate: true,
32511              /**
32512              * @event beforesync
32513              * Fires before the textarea is updated with content from the editor iframe. Return false
32514              * to cancel the sync.
32515              * @param {Roo.bootstrap.form.HtmlEditor} this
32516              * @param {String} html
32517              */
32518             beforesync: true,
32519              /**
32520              * @event beforepush
32521              * Fires before the iframe editor is updated with content from the textarea. Return false
32522              * to cancel the push.
32523              * @param {Roo.bootstrap.form.HtmlEditor} this
32524              * @param {String} html
32525              */
32526             beforepush: true,
32527              /**
32528              * @event sync
32529              * Fires when the textarea is updated with content from the editor iframe.
32530              * @param {Roo.bootstrap.form.HtmlEditor} this
32531              * @param {String} html
32532              */
32533             sync: true,
32534              /**
32535              * @event push
32536              * Fires when the iframe editor is updated with content from the textarea.
32537              * @param {Roo.bootstrap.form.HtmlEditor} this
32538              * @param {String} html
32539              */
32540             push: true,
32541              /**
32542              * @event editmodechange
32543              * Fires when the editor switches edit modes
32544              * @param {Roo.bootstrap.form.HtmlEditor} this
32545              * @param {Boolean} sourceEdit True if source edit, false if standard editing.
32546              */
32547             editmodechange: true,
32548             /**
32549              * @event editorevent
32550              * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
32551              * @param {Roo.bootstrap.form.HtmlEditor} this
32552              */
32553             editorevent: true,
32554             /**
32555              * @event firstfocus
32556              * Fires when on first focus - needed by toolbars..
32557              * @param {Roo.bootstrap.form.HtmlEditor} this
32558              */
32559             firstfocus: true,
32560             /**
32561              * @event autosave
32562              * Auto save the htmlEditor value as a file into Events
32563              * @param {Roo.bootstrap.form.HtmlEditor} this
32564              */
32565             autosave: true,
32566             /**
32567              * @event savedpreview
32568              * preview the saved version of htmlEditor
32569              * @param {Roo.bootstrap.form.HtmlEditor} this
32570              */
32571             savedpreview: true,
32572              /**
32573             * @event stylesheetsclick
32574             * Fires when press the Sytlesheets button
32575             * @param {Roo.HtmlEditorCore} this
32576             */
32577             stylesheetsclick: true,
32578             /**
32579             * @event paste
32580             * Fires when press user pastes into the editor
32581             * @param {Roo.HtmlEditorCore} this
32582             */
32583             paste: true,
32584             /**
32585             * @event imageadd
32586             * Fires when on any editor when an image is added (excluding paste)
32587             * @param {Roo.bootstrap.form.HtmlEditor} this
32588             */
32589            imageadd: true ,
32590             /**
32591             * @event imageupdated
32592             * Fires when on any editor when an image is changed (excluding paste)
32593             * @param {Roo.bootstrap.form.HtmlEditor} this
32594             * @param {HTMLElement} img could also be a figure if blocks are enabled
32595             */
32596            imageupdate: true ,
32597            /**
32598             * @event imagedelete
32599             * Fires when on any editor when an image is deleted
32600             * @param {Roo.bootstrap.form.HtmlEditor} this
32601             * @param {HTMLElement} img could also be a figure if blocks are enabled
32602             * @param {HTMLElement} oldSrc source of image being replaced
32603             */
32604            imagedelete: true  
32605     });
32606     Roo.bootstrap.form.HtmlEditor.superclass.constructor.call(this, config);
32607     if (!this.toolbars) {
32608         this.toolbars = [];
32609     }
32610     
32611     this.editorcore = new Roo.HtmlEditorCore(Roo.apply({ owner : this} , config));
32612     
32613 };
32614
32615
32616 Roo.extend(Roo.bootstrap.form.HtmlEditor, Roo.bootstrap.form.TextArea,  {
32617     
32618     
32619       /**
32620      * @cfg {Array|boolean} toolbars Array of toolbars, or names of toolbars. - true for standard, and false for none.
32621      */
32622     toolbars : true,
32623     
32624      /**
32625     * @cfg {Array} buttons Array of toolbar's buttons. - defaults to empty
32626     */
32627     btns : [],
32628    
32629      /**
32630      * @cfg {String} resize  (none|both|horizontal|vertical) - css resize of element
32631      */
32632     resize : false,
32633      /**
32634      * @cfg {Number} height (in pixels)
32635      */   
32636     height: 300,
32637    /**
32638      * @cfg {Number} width (in pixels)
32639      */   
32640     width: false,
32641     
32642     /**
32643      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
32644      * 
32645      */
32646     stylesheets: false,
32647     
32648     // id of frame..
32649     frameId: false,
32650     
32651     // private properties
32652     validationEvent : false,
32653     deferHeight: true,
32654     initialized : false,
32655     activated : false,
32656     
32657     onFocus : Roo.emptyFn,
32658     iframePad:3,
32659     hideMode:'offsets',
32660     
32661     tbContainer : false,
32662     
32663     bodyCls : '',
32664
32665     linkDialogCls : '',
32666     
32667     toolbarContainer :function() {
32668         return this.wrap.select('.x-html-editor-tb',true).first();
32669     },
32670
32671     /**
32672      * Protected method that will not generally be called directly. It
32673      * is called when the editor creates its toolbar. Override this method if you need to
32674      * add custom toolbar buttons.
32675      * @param {HtmlEditor} editor
32676      */
32677     createToolbar : function()
32678     {
32679         //Roo.log('renewing');
32680         //Roo.log("create toolbars");
32681         if (this.toolbars === false) {
32682             return;
32683         }
32684         if (this.toolbars === true) {
32685             this.toolbars = [ 'Standard' ];
32686         }
32687         
32688         var ar = Array.from(this.toolbars);
32689         this.toolbars = [];
32690         ar.forEach(function(t,i) {
32691             if (typeof(t) == 'string') {
32692                 t = {
32693                     xtype : t
32694                 };
32695             }
32696             if (typeof(t) == 'object' && typeof(t.xtype) == 'string') {
32697                 t.editor = this;
32698                 t.xns = t.xns || Roo.bootstrap.form.HtmlEditorToolbar;
32699                 t = Roo.factory(t);
32700             }
32701             this.toolbars[i] = t;
32702             this.toolbars[i].render(this.toolbarContainer());
32703         }, this);
32704         
32705         
32706     },
32707
32708      
32709     // private
32710     onRender : function(ct, position)
32711     {
32712        // Roo.log("Call onRender: " + this.xtype);
32713         var _t = this;
32714         Roo.bootstrap.form.HtmlEditor.superclass.onRender.call(this, ct, position);
32715       
32716         this.wrap = this.inputEl().wrap({
32717             cls:'x-html-editor-wrap', cn:{cls:'x-html-editor-tb'}
32718         });
32719         
32720         this.editorcore.onRender(ct, position);
32721          
32722          
32723         this.createToolbar(this);
32724        
32725         
32726           
32727         
32728     },
32729
32730     // private
32731     onResize : function(w, h)
32732     {
32733         Roo.log('resize: ' +w + ',' + h );
32734         Roo.bootstrap.form.HtmlEditor.superclass.onResize.apply(this, arguments);
32735         var ew = false;
32736         var eh = false;
32737         
32738         if(this.inputEl() ){
32739             if(typeof w == 'number'){
32740                 var aw = w - this.wrap.getFrameWidth('lr');
32741                 this.inputEl().setWidth(this.adjustWidth('textarea', aw));
32742                 ew = aw;
32743             }
32744             if(typeof h == 'number'){
32745                  var tbh = -11;  // fixme it needs to tool bar size!
32746                 for (var i =0; i < this.toolbars.length;i++) {
32747                     // fixme - ask toolbars for heights?
32748                     tbh += this.toolbars[i].el.getHeight();
32749                     //if (this.toolbars[i].footer) {
32750                     //    tbh += this.toolbars[i].footer.el.getHeight();
32751                     //}
32752                 }
32753               
32754                 
32755                 
32756                 
32757                 
32758                 var ah = h - this.wrap.getFrameWidth('tb') - tbh;// this.tb.el.getHeight();
32759                 ah -= 5; // knock a few pixes off for look..
32760                 this.inputEl().setHeight(this.adjustWidth('textarea', ah));
32761                 var eh = ah;
32762             }
32763         }
32764         Roo.log('onResize:' + [w,h,ew,eh].join(',') );
32765         this.editorcore.onResize(ew,eh);
32766         
32767     },
32768
32769     /**
32770      * Toggles the editor between standard and source edit mode.
32771      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
32772      */
32773     toggleSourceEdit : function(sourceEditMode)
32774     {
32775         this.editorcore.toggleSourceEdit(sourceEditMode);
32776         
32777         if(this.editorcore.sourceEditMode){
32778             Roo.log('editor - showing textarea');
32779             
32780 //            Roo.log('in');
32781 //            Roo.log(this.syncValue());
32782             this.syncValue();
32783             this.inputEl().removeClass(['hide', 'x-hidden']);
32784             this.inputEl().dom.removeAttribute('tabIndex');
32785             this.inputEl().focus();
32786         }else{
32787             Roo.log('editor - hiding textarea');
32788 //            Roo.log('out')
32789 //            Roo.log(this.pushValue()); 
32790             this.pushValue();
32791             
32792             this.inputEl().addClass(['hide', 'x-hidden']);
32793             this.inputEl().dom.setAttribute('tabIndex', -1);
32794             //this.deferFocus();
32795         }
32796          
32797         //if(this.resizable){
32798         //    this.setSize(this.wrap.getSize());
32799         //}
32800         
32801         this.fireEvent('editmodechange', this, this.editorcore.sourceEditMode);
32802     },
32803  
32804     // private (for BoxComponent)
32805     adjustSize : Roo.BoxComponent.prototype.adjustSize,
32806
32807     // private (for BoxComponent)
32808     getResizeEl : function(){
32809         return this.wrap;
32810     },
32811
32812     // private (for BoxComponent)
32813     getPositionEl : function(){
32814         return this.wrap;
32815     },
32816
32817     // private
32818     initEvents : function(){
32819         this.originalValue = this.getValue();
32820     },
32821
32822 //    /**
32823 //     * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
32824 //     * @method
32825 //     */
32826 //    markInvalid : Roo.emptyFn,
32827 //    /**
32828 //     * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
32829 //     * @method
32830 //     */
32831 //    clearInvalid : Roo.emptyFn,
32832
32833     setValue : function(v){
32834         Roo.bootstrap.form.HtmlEditor.superclass.setValue.call(this, v);
32835         this.editorcore.pushValue();
32836     },
32837
32838      
32839     // private
32840     deferFocus : function(){
32841         this.focus.defer(10, this);
32842     },
32843
32844     // doc'ed in Field
32845     focus : function(){
32846         this.editorcore.focus();
32847         
32848     },
32849       
32850
32851     // private
32852     onDestroy : function(){
32853         
32854         
32855         
32856         if(this.rendered){
32857             
32858             for (var i =0; i < this.toolbars.length;i++) {
32859                 // fixme - ask toolbars for heights?
32860                 this.toolbars[i].onDestroy();
32861             }
32862             
32863             this.wrap.dom.innerHTML = '';
32864             this.wrap.remove();
32865         }
32866     },
32867
32868     // private
32869     onFirstFocus : function(){
32870         //Roo.log("onFirstFocus");
32871         this.editorcore.onFirstFocus();
32872          for (var i =0; i < this.toolbars.length;i++) {
32873             this.toolbars[i].onFirstFocus();
32874         }
32875         
32876     },
32877     
32878     // private
32879     syncValue : function()
32880     {   
32881         this.editorcore.syncValue();
32882     },
32883     
32884     pushValue : function()
32885     {   
32886         this.editorcore.pushValue();
32887     }
32888      
32889     
32890     // hide stuff that is not compatible
32891     /**
32892      * @event blur
32893      * @hide
32894      */
32895     /**
32896      * @event change
32897      * @hide
32898      */
32899     /**
32900      * @event focus
32901      * @hide
32902      */
32903     /**
32904      * @event specialkey
32905      * @hide
32906      */
32907     /**
32908      * @cfg {String} fieldClass @hide
32909      */
32910     /**
32911      * @cfg {String} focusClass @hide
32912      */
32913     /**
32914      * @cfg {String} autoCreate @hide
32915      */
32916     /**
32917      * @cfg {String} inputType @hide
32918      */
32919      
32920     /**
32921      * @cfg {String} invalidText @hide
32922      */
32923     /**
32924      * @cfg {String} msgFx @hide
32925      */
32926     /**
32927      * @cfg {String} validateOnBlur @hide
32928      */
32929 });
32930  
32931     
32932    
32933    
32934    
32935       
32936 /**
32937  * @class Roo.bootstrap.form.HtmlEditorToolbar.Standard
32938  * @parent Roo.bootstrap.form.HtmlEditor
32939  * @extends Roo.bootstrap.nav.Simplebar
32940  * Basic Toolbar
32941  * 
32942  * @example
32943  * Usage:
32944  *
32945  new Roo.bootstrap.form.HtmlEditor({
32946     ....
32947     toolbars : [
32948         new Roo.bootstrap.form.HtmlEditorToolbar.Standard({
32949             disable : { fonts: 1 , format: 1, ..., ... , ...],
32950             btns : [ .... ]
32951         })
32952     }
32953      
32954  * 
32955  * @cfg {Object} disable List of elements to disable..
32956  * @cfg {Array} btns List of additional buttons.
32957  * 
32958  * 
32959  * NEEDS Extra CSS? 
32960  * .x-html-editor-tb .x-edit-none .x-btn-text { background: none; }
32961  */
32962  
32963 Roo.bootstrap.form.HtmlEditorToolbar.Standard = function(config)
32964 {
32965     
32966     Roo.apply(this, config);
32967     
32968     // default disabled, based on 'good practice'..
32969     this.disable = this.disable || {};
32970     Roo.applyIf(this.disable, {
32971         fontSize : true,
32972         colors : true,
32973         specialElements : true
32974     });
32975     Roo.bootstrap.form.HtmlEditorToolbar.Standard.superclass.constructor.call(this, config);
32976     
32977     this.editor = config.editor;
32978     this.editorcore = config.editor.editorcore;
32979     
32980     this.buttons   = new Roo.util.MixedCollection(false, function(o) { return o.btnid; });
32981     
32982     //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
32983     // dont call parent... till later.
32984 }
32985 Roo.extend(Roo.bootstrap.form.HtmlEditorToolbar.Standard, Roo.bootstrap.nav.Simplebar,  {
32986      
32987     bar : true,
32988     
32989     editor : false,
32990     editorcore : false,
32991     
32992     
32993     formats : [
32994         "p" ,  
32995         "h1","h2","h3","h4","h5","h6", 
32996         "pre", "code", 
32997         "abbr", "acronym", "address", "cite", "samp", "var",
32998         'div','span'
32999     ],
33000     
33001     
33002     deleteBtn: false,
33003     
33004     onRender : function(ct, position)
33005     {
33006        // Roo.log("Call onRender: " + this.xtype);
33007         
33008        Roo.bootstrap.form.HtmlEditorToolbar.Standard.superclass.onRender.call(this, ct, position);
33009        Roo.log(this.el);
33010        this.el.dom.style.marginBottom = '0';
33011        var _this = this;
33012        var editorcore = this.editorcore;
33013        var editor= this.editor;
33014        
33015        var children = [];
33016        var btn = function(id, cmd , toggle, handler, html){
33017        
33018             var  event = toggle ? 'toggle' : 'click';
33019        
33020             var a = {
33021                 size : 'sm',
33022                 xtype: 'Button',
33023                 xns: Roo.bootstrap,
33024                 //glyphicon : id,
33025                 btnid : id,
33026                 fa: id,
33027                 cls : 'roo-html-editor-btn-' + id,
33028                 cmd : cmd, // why id || cmd
33029                 enableToggle: toggle !== false,
33030                 html : html || '',
33031                 pressed : toggle ? false : null,
33032                 listeners : {}
33033             };
33034             a.listeners[toggle ? 'toggle' : 'click'] = function() {
33035                 handler ? handler.call(_this,this) :_this.onBtnClick.call(_this, cmd ||  id);
33036             };
33037             children.push(a);
33038             return a;
33039        }
33040        
33041     //    var cb_box = function...
33042         
33043         var style = {
33044                 xtype: 'Button',
33045                 size : 'sm',
33046                 xns: Roo.bootstrap,
33047                 fa : 'font',
33048                 cls : 'roo-html-editor-font-chooser',
33049                 //html : 'submit'
33050                 menu : {
33051                     xtype: 'Menu',
33052                     xns: Roo.bootstrap,
33053                     items:  []
33054                 }
33055         };
33056         Roo.each(this.formats, function(f) {
33057             style.menu.items.push({
33058                 xtype :'MenuItem',
33059                 xns: Roo.bootstrap,
33060                 html : '<'+ f+' style="margin:2px">'+f +'</'+ f+'>',
33061                 tagname : f,
33062                 listeners : {
33063                     click : function()
33064                     {
33065                         editorcore.insertTag(this.tagname);
33066                         editor.focus();
33067                     }
33068                 }
33069                 
33070             });
33071         });
33072         children.push(style);   
33073         
33074         btn('bold',         'bold',true);
33075         btn('italic',       'italic',true);
33076         btn('underline',     'underline',true);
33077         btn('align-left',   'justifyleft',true);
33078         btn('align-center', 'justifycenter',true);
33079         btn('align-right' , 'justifyright',true);
33080         btn('link', false, true, this.onLinkClick);
33081         
33082         
33083         btn('image', false, true, this.onImageClick);
33084         btn('list','insertunorderedlist',true);
33085         btn('list-ol','insertorderedlist',true);
33086
33087         btn('pencil', false,true, function(btn){
33088                 Roo.log(this);
33089                 this.toggleSourceEdit(btn.pressed);
33090         });
33091         
33092         if (this.editor.btns.length > 0) {
33093             for (var i = 0; i<this.editor.btns.length; i++) {
33094                 children.push(this.editor.btns[i]);
33095             }
33096         }
33097         
33098         
33099          
33100         this.xtype = 'NavSimplebar'; // why?
33101         
33102         for(var i=0;i< children.length;i++) {
33103             
33104             this.buttons.add(this.addxtypeChild(children[i]));
33105             
33106         }
33107         this.buildToolbarDelete();
33108
33109         editor.on('editorevent', this.updateToolbar, this);
33110     },
33111     
33112     buildToolbarDelete : function()
33113     {
33114         
33115        /* this.addxtypeChild({
33116             xtype : 'Element',
33117             xns : Roo.bootstrap,
33118             cls : 'roo-htmleditor-fill'
33119         });
33120         */
33121         this.deleteBtn = this.addxtypeChild({
33122             size : 'sm',
33123             xtype: 'Button',
33124             xns: Roo.bootstrap,
33125             fa: 'trash',
33126             listeners : {
33127                 click : this.onDelete.createDelegate(this)
33128             }
33129         });
33130         this.deleteBtn.hide();     
33131         
33132     },
33133     
33134     onImageClick : function()
33135     {
33136         if (this.input) {
33137             this.input.un('change', this.onFileSelected, this);
33138         }
33139         this.input = Roo.get(document.body).createChild({ 
33140           tag: 'input', 
33141           type : 'file', 
33142           style : 'display:none', 
33143           multiple: 'multiple'
33144        });
33145         this.input.on('change', this.onFileSelected, this);
33146         this.input.dom.click();
33147     },
33148     
33149     onFileSelected : function(e)
33150     {
33151          e.preventDefault();
33152         
33153         if(typeof(this.input.dom.files) == 'undefined' || !this.input.dom.files.length){
33154             return;
33155         }
33156     
33157          
33158         this.addFiles(Array.prototype.slice.call(this.input.dom.files), false);
33159     },
33160     
33161     addFiles : function(far, fire_add) {
33162
33163          
33164         var editor =  this.editorcore;
33165   
33166         if (!far.length) {
33167             if (fire_add) {
33168                 this.editor.syncValue();
33169                 editor.owner.fireEvent('editorevent', editor.owner, false);
33170                 editor.owner.fireEvent('imageadd', editor.owner, false);
33171             }
33172             return;
33173         }
33174         
33175         var f = far.pop();
33176         
33177         if (!f.type.match(/^image/)) {
33178             this.addFiles(far, fire_add);
33179             return;
33180         }
33181          
33182         var sn = this.selectedNode;
33183         
33184         var bl = sn  && this.editorcore.enableBlocks ? Roo.htmleditor.Block.factory(sn) : false;
33185         
33186         
33187         var reader = new FileReader();
33188         reader.addEventListener('load', (function() {
33189             if (bl) {
33190                 var oldSrc = bl.image_src;
33191                 bl.image_src = reader.result;
33192                 //bl.caption = f.name;
33193                 bl.updateElement(sn);
33194                 this.editor.syncValue();
33195                 editor.owner.fireEvent('editorevent', editor.owner, false);
33196                 editor.owner.fireEvent('imageupdate', editor.owner, sn, oldSrc);
33197                 // we only do the first file!! and replace.
33198                 return;
33199             }
33200             if (this.editorcore.enableBlocks) {
33201                 var fig = new Roo.htmleditor.BlockFigure({
33202                     image_src :  reader.result,
33203                     caption : '',
33204                     caption_display : 'none'  //default to hide captions..
33205                  });
33206                 editor.insertAtCursor(fig.toHTML());
33207                 this.addFiles(far, true);
33208                 return;
33209             }
33210             // just a standard img..
33211             if (sn && sn.tagName.toUpperCase() == 'IMG') {
33212                 var oldSrc = sn.src;
33213                 sn.src = reader.result;
33214                 this.editor.syncValue();
33215                 editor.owner.fireEvent('editorevent', editor.owner, false);
33216                 editor.owner.fireEvent('imageupdate', editor.owner, sn, oldSrc);
33217                 return;
33218             }
33219             editor.insertAtCursor('<img src="' + reader.result +'">');
33220             this.addFiles(far, true);
33221             
33222         }).createDelegate(this));
33223         reader.readAsDataURL(f);
33224         
33225     
33226      },
33227     
33228     
33229     onBtnClick : function(id)
33230     {
33231        this.editorcore.relayCmd(id);
33232        this.editorcore.focus();
33233     },
33234     
33235     onLinkClick : function(btn) {
33236         var url = this.selectedNode && this.selectedNode.tagName.toUpperCase() == 'A' ?
33237                 this.selectedNode.getAttribute('href') : '';
33238             
33239         Roo.bootstrap.MessageBox.show({
33240             title : "Add / Edit Link URL",
33241             msg : "Enter the URL for the link",
33242             buttons: Roo.bootstrap.MessageBox.OKCANCEL,
33243             minWidth: 250,
33244             scope : this,
33245             prompt:true,
33246             multiline: false,
33247             modal : true,
33248             value : url,
33249             fn:  function(pressed, newurl) {
33250                 if (pressed != 'ok') {
33251                     this.editorcore.focus();
33252                     return;
33253                 }
33254                 if (url != '') {
33255                     this.selectedNode.setAttribute('href', newurl);
33256                     this.editor.syncValue();
33257                     return;
33258                 }
33259                 if(newurl && newurl .match(/http(s):\/\/.+/)) {
33260                     this.editorcore.relayCmd('createlink', newurl);
33261                 }
33262                 this.editorcore.focus();
33263             },
33264             cls : this.editorcore.linkDialogCls
33265         });
33266     },
33267     /**
33268      * Protected method that will not generally be called directly. It triggers
33269      * a toolbar update by reading the markup state of the current selection in the editor.
33270      */
33271     updateToolbar: function(editor ,ev, sel){
33272
33273         if(!this.editorcore.activated){
33274             this.editor.onFirstFocus(); // is this neeed?
33275             return;
33276         }
33277
33278         var btns = this.buttons; 
33279         var doc = this.editorcore.doc;
33280         var hasToggle  = false;
33281         btns.each(function(e) {
33282             if (e.enableToggle && e.cmd) {
33283                 hasToggle = hasToggle  || (['align-left', 'align-right', 'align-center', 'image' , 'link', 'underline'].indexOf(e.btnid) < 0 && doc.queryCommandState(e.cmd));
33284                 e.setActive(doc.queryCommandState(e.cmd));
33285             }
33286         }, this);
33287         
33288         
33289         if (ev &&
33290             (ev.type == 'mouseup' || ev.type == 'click' ) &&
33291             ev.target && ev.target.tagName != 'BODY' ) { // && ev.target.tagName == 'IMG') {
33292             // they have click on an image...
33293             // let's see if we can change the selection...
33294             sel = ev.target;
33295             
33296         }
33297         
33298         var ans = this.editorcore.getAllAncestors();
33299         if (!sel) { 
33300             sel = ans.length ? (ans[0] ?  ans[0]  : ans[1]) : this.editorcore.doc.body;
33301             sel = sel ? sel : this.editorcore.doc.body;
33302             sel = sel.tagName.length ? sel : this.editorcore.doc.body;
33303             
33304         }
33305         
33306         var lastSel = this.selectedNode;
33307         this.selectedNode = sel;
33308          
33309         // ok see if we are editing a block?
33310         
33311         var db = false;
33312         // you are not actually selecting the block.
33313         if (sel && sel.hasAttribute('data-block')) {
33314             db = sel;
33315         } else if (sel && sel.closest('[data-block]')) {
33316             db = sel.closest('[data-block]');
33317         }
33318         
33319         Array.from(this.editorcore.doc.body.querySelectorAll('.roo-ed-selection')).forEach(function(e) {
33320             e.classList.remove('roo-ed-selection');
33321         });
33322         
33323         var block = false;
33324         if (db && this.editorcore.enableBlocks) {
33325             block = Roo.htmleditor.Block.factory(db);
33326             
33327             if (block) {
33328                 db.className =  (db.classList.length > 0  ? db.className + ' ' : '') +
33329                     ' roo-ed-selection';
33330                 sel = this.selectedNode = db;
33331             }
33332         }
33333         
33334         // highlight the 'a'..
33335         var tn = sel && sel.tagName.toUpperCase() || '';
33336         if (!block && sel && tn != 'A') {
33337             var asel = sel.closest('A');
33338             if (asel) {
33339                 sel = asel;
33340             }
33341         }
33342        
33343         btns.get('link').setActive(tn == 'A' && this.selectedNode.hasAttribute('href'));
33344         btns.get('image').setActive(tn == 'IMG' || this.editorcore.enableBlocks && tn == 'FIGURE');
33345         btns.get('underline').setActive(tn == 'U' || sel.closest('u') ? true : false);
33346         
33347         Roo.bootstrap.menu.Manager.hideAll();
33348          
33349         
33350         
33351         
33352         
33353         // handle delete button..
33354         if (hasToggle || (tn.length && tn == 'BODY')) {
33355             this.deleteBtn.hide();
33356             return;
33357             
33358         }
33359         this.deleteBtn.show();
33360         
33361         
33362         
33363         //this.editorsyncValue();
33364     },
33365     onFirstFocus: function() {
33366         this.buttons.each(function(item){
33367            item.enable();
33368         });
33369     },
33370     
33371     onDelete : function()
33372     {
33373         var range = this.editorcore.createRange();
33374         var selection = this.editorcore.getSelection();
33375         var sn = this.selectedNode;
33376         range.setStart(sn,0);
33377         range.setEnd(sn,0); 
33378         
33379         
33380         if (sn.hasAttribute('data-block')) {
33381             var block = Roo.htmleditor.Block.factory(this.selectedNode);
33382             if (block) {
33383                 sn = block.removeNode();
33384                 sn.parentNode.removeChild(sn);
33385                 selection.removeAllRanges();
33386                 selection.addRange(range);
33387                 this.updateToolbar(null, null, null);
33388                 if (sn.tagName.toUpperCase() == 'FIGURE') {
33389                     this.editor.syncValue();
33390                     this.editor.fireEvent('imagedelete', this.editor, sn);
33391                 }
33392                 
33393                 this.selectedNode = false;
33394                 this.editorcore.fireEditorEvent(false);
33395                 return;
33396             }   
33397              
33398         }
33399         if (!sn) {
33400             return; // should not really happen..
33401         }
33402         if (sn && sn.tagName == 'BODY') {
33403             return;
33404         }
33405         var stn =  sn.childNodes[0] || sn.nextSibling || sn.previousSibling || sn.parentNode;
33406         
33407         // remove and keep parents.
33408         a = new Roo.htmleditor.FilterKeepChildren({tag : false});
33409         a.replaceTag(sn);
33410         
33411         selection.removeAllRanges();
33412         selection.addRange(range);
33413         if (sn.tagName.toUpperCase() == 'IMG"') {
33414             this.editor.syncValue();
33415             this.editor.fireEvent('imagedelete', this.editor, sn);
33416         }
33417         
33418         this.selectedNode = false;
33419         this.editorcore.fireEditorEvent(false);
33420         
33421         
33422     },
33423     
33424     
33425     toggleSourceEdit : function(sourceEditMode){
33426         
33427           
33428         if(sourceEditMode){
33429             Roo.log("disabling buttons");
33430            this.buttons.each( function(item){
33431                 if(item.cmd != 'pencil'){
33432                     item.disable();
33433                 }
33434             });
33435           
33436         }else{
33437             Roo.log("enabling buttons");
33438             if(this.editorcore.initialized){
33439                 this.buttons.each( function(item){
33440                     item.enable();
33441                 });
33442             }
33443             
33444         }
33445         Roo.log("calling toggole on editor");
33446         // tell the editor that it's been pressed..
33447         this.editor.toggleSourceEdit(sourceEditMode);
33448        
33449     }
33450 });
33451
33452
33453
33454
33455  
33456 /*
33457  * - LGPL
33458  */
33459
33460 /**
33461  * @class Roo.bootstrap.form.Markdown
33462  * @extends Roo.bootstrap.form.TextArea
33463  * Bootstrap Showdown editable area
33464  * @cfg {string} content
33465  * 
33466  * @constructor
33467  * Create a new Showdown
33468  */
33469
33470 Roo.bootstrap.form.Markdown = function(config){
33471     Roo.bootstrap.form.Markdown.superclass.constructor.call(this, config);
33472    
33473 };
33474
33475 Roo.extend(Roo.bootstrap.form.Markdown, Roo.bootstrap.form.TextArea,  {
33476     
33477     editing :false,
33478     
33479     initEvents : function()
33480     {
33481         
33482         Roo.bootstrap.form.TextArea.prototype.initEvents.call(this);
33483         this.markdownEl = this.el.createChild({
33484             cls : 'roo-markdown-area'
33485         });
33486         this.inputEl().addClass('d-none');
33487         if (this.getValue() == '') {
33488             this.markdownEl.dom.innerHTML = String.format('<span class="roo-placeholder">{0}</span>', this.placeholder || '');
33489             
33490         } else {
33491             this.markdownEl.dom.innerHTML = Roo.Markdown.toHtml(Roo.util.Format.htmlEncode(this.getValue()));
33492         }
33493         this.markdownEl.on('click', this.toggleTextEdit, this);
33494         this.on('blur', this.toggleTextEdit, this);
33495         this.on('specialkey', this.resizeTextArea, this);
33496     },
33497     
33498     toggleTextEdit : function()
33499     {
33500         var sh = this.markdownEl.getHeight();
33501         this.inputEl().addClass('d-none');
33502         this.markdownEl.addClass('d-none');
33503         if (!this.editing) {
33504             // show editor?
33505             this.inputEl().setHeight(Math.min(500, Math.max(sh,(this.getValue().split("\n").length+1) * 30)));
33506             this.inputEl().removeClass('d-none');
33507             this.inputEl().focus();
33508             this.editing = true;
33509             return;
33510         }
33511         // show showdown...
33512         this.updateMarkdown();
33513         this.markdownEl.removeClass('d-none');
33514         this.editing = false;
33515         return;
33516     },
33517     updateMarkdown : function()
33518     {
33519         if (this.getValue() == '') {
33520             this.markdownEl.dom.innerHTML = String.format('<span class="roo-placeholder">{0}</span>', this.placeholder || '');
33521             return;
33522         }
33523  
33524         this.markdownEl.dom.innerHTML = Roo.Markdown.toHtml(Roo.util.Format.htmlEncode(this.getValue()));
33525     },
33526     
33527     resizeTextArea: function () {
33528         
33529         var sh = 100;
33530         Roo.log([sh, this.getValue().split("\n").length * 30]);
33531         this.inputEl().setHeight(Math.min(500, Math.max(sh, (this.getValue().split("\n").length +1) * 30)));
33532     },
33533     setValue : function(val)
33534     {
33535         Roo.bootstrap.form.TextArea.prototype.setValue.call(this,val);
33536         if (!this.editing) {
33537             this.updateMarkdown();
33538         }
33539         
33540     },
33541     focus : function()
33542     {
33543         if (!this.editing) {
33544             this.toggleTextEdit();
33545         }
33546         
33547     }
33548
33549
33550 });/*
33551  * Based on:
33552  * Ext JS Library 1.1.1
33553  * Copyright(c) 2006-2007, Ext JS, LLC.
33554  *
33555  * Originally Released Under LGPL - original licence link has changed is not relivant.
33556  *
33557  * Fork - LGPL
33558  * <script type="text/javascript">
33559  */
33560  
33561 /**
33562  * @class Roo.bootstrap.PagingToolbar
33563  * @extends Roo.bootstrap.nav.Simplebar
33564  * A specialized toolbar that is bound to a {@link Roo.data.Store} and provides automatic paging controls.
33565  * @constructor
33566  * Create a new PagingToolbar
33567  * @param {Object} config The config object
33568  * @param {Roo.data.Store} store
33569  */
33570 Roo.bootstrap.PagingToolbar = function(config)
33571 {
33572     // old args format still supported... - xtype is prefered..
33573         // created from xtype...
33574     
33575     this.ds = config.dataSource;
33576     
33577     if (config.store && !this.ds) {
33578         this.store= Roo.factory(config.store, Roo.data);
33579         this.ds = this.store;
33580         this.ds.xmodule = this.xmodule || false;
33581     }
33582     
33583     this.toolbarItems = [];
33584     if (config.items) {
33585         this.toolbarItems = config.items;
33586     }
33587     
33588     Roo.bootstrap.PagingToolbar.superclass.constructor.call(this, config);
33589     
33590     this.cursor = 0;
33591     
33592     if (this.ds) { 
33593         this.bind(this.ds);
33594     }
33595     
33596     if (Roo.bootstrap.version == 4) {
33597         this.navgroup = new Roo.bootstrap.ButtonGroup({ cls: 'pagination' });
33598     } else {
33599         this.navgroup = new Roo.bootstrap.nav.Group({ cls: 'pagination' });
33600     }
33601     
33602 };
33603
33604 Roo.extend(Roo.bootstrap.PagingToolbar, Roo.bootstrap.nav.Simplebar, {
33605     /**
33606      * @cfg {Roo.bootstrap.Button} buttons[]
33607      * Buttons for the toolbar
33608      */
33609      /**
33610      * @cfg {Roo.data.Store} store
33611      * The underlying data store providing the paged data
33612      */
33613     /**
33614      * @cfg {String/HTMLElement/Element} container
33615      * container The id or element that will contain the toolbar
33616      */
33617     /**
33618      * @cfg {Boolean} displayInfo
33619      * True to display the displayMsg (defaults to false)
33620      */
33621     /**
33622      * @cfg {Number} pageSize
33623      * The number of records to display per page (defaults to 20)
33624      */
33625     pageSize: 20,
33626     /**
33627      * @cfg {String} displayMsg
33628      * The paging status message to display (defaults to "Displaying {start} - {end} of {total}")
33629      */
33630     displayMsg : 'Displaying {0} - {1} of {2}',
33631     /**
33632      * @cfg {String} emptyMsg
33633      * The message to display when no records are found (defaults to "No data to display")
33634      */
33635     emptyMsg : 'No data to display',
33636     /**
33637      * Customizable piece of the default paging text (defaults to "Page")
33638      * @type String
33639      */
33640     beforePageText : "Page",
33641     /**
33642      * Customizable piece of the default paging text (defaults to "of %0")
33643      * @type String
33644      */
33645     afterPageText : "of {0}",
33646     /**
33647      * Customizable piece of the default paging text (defaults to "First Page")
33648      * @type String
33649      */
33650     firstText : "First Page",
33651     /**
33652      * Customizable piece of the default paging text (defaults to "Previous Page")
33653      * @type String
33654      */
33655     prevText : "Previous Page",
33656     /**
33657      * Customizable piece of the default paging text (defaults to "Next Page")
33658      * @type String
33659      */
33660     nextText : "Next Page",
33661     /**
33662      * Customizable piece of the default paging text (defaults to "Last Page")
33663      * @type String
33664      */
33665     lastText : "Last Page",
33666     /**
33667      * Customizable piece of the default paging text (defaults to "Refresh")
33668      * @type String
33669      */
33670     refreshText : "Refresh",
33671
33672     buttons : false,
33673     // private
33674     onRender : function(ct, position) 
33675     {
33676         Roo.bootstrap.PagingToolbar.superclass.onRender.call(this, ct, position);
33677         this.navgroup.parentId = this.id;
33678         this.navgroup.onRender(this.el, null);
33679         // add the buttons to the navgroup
33680         
33681         if(this.displayInfo){
33682             this.el.select('ul.navbar-nav',true).first().createChild({cls:'x-paging-info'});
33683             this.displayEl = this.el.select('.x-paging-info', true).first();
33684 //            var navel = this.navgroup.addItem( { tagtype : 'span', html : '', cls : 'x-paging-info', preventDefault : true } );
33685 //            this.displayEl = navel.el.select('span',true).first();
33686         }
33687         
33688         var _this = this;
33689         
33690         if(this.buttons){
33691             Roo.each(_this.buttons, function(e){ // this might need to use render????
33692                Roo.factory(e).render(_this.el);
33693             });
33694         }
33695             
33696         Roo.each(_this.toolbarItems, function(e) {
33697             _this.navgroup.addItem(e);
33698         });
33699         
33700         
33701         this.first = this.navgroup.addItem({
33702             tooltip: this.firstText,
33703             cls: "prev btn-outline-secondary",
33704             html : ' <i class="fa fa-step-backward"></i>',
33705             disabled: true,
33706             preventDefault: true,
33707             listeners : { click : this.onClick.createDelegate(this, ["first"]) }
33708         });
33709         
33710         this.prev =  this.navgroup.addItem({
33711             tooltip: this.prevText,
33712             cls: "prev btn-outline-secondary",
33713             html : ' <i class="fa fa-backward"></i>',
33714             disabled: true,
33715             preventDefault: true,
33716             listeners : { click :  this.onClick.createDelegate(this, ["prev"]) }
33717         });
33718     //this.addSeparator();
33719         
33720         
33721         var field = this.navgroup.addItem( {
33722             tagtype : 'span',
33723             cls : 'x-paging-position  btn-outline-secondary',
33724              disabled: true,
33725             html : this.beforePageText  +
33726                 '<input type="text" size="3" value="1" class="x-grid-page-number">' +
33727                 '<span class="x-paging-after">' +  String.format(this.afterPageText, 1) + '</span>'
33728          } ); //?? escaped?
33729         
33730         this.field = field.el.select('input', true).first();
33731         this.field.on("keydown", this.onPagingKeydown, this);
33732         this.field.on("focus", function(){this.dom.select();});
33733     
33734     
33735         this.afterTextEl =  field.el.select('.x-paging-after',true).first();
33736         //this.field.setHeight(18);
33737         //this.addSeparator();
33738         this.next = this.navgroup.addItem({
33739             tooltip: this.nextText,
33740             cls: "next btn-outline-secondary",
33741             html : ' <i class="fa fa-forward"></i>',
33742             disabled: true,
33743             preventDefault: true,
33744             listeners : { click :  this.onClick.createDelegate(this, ["next"]) }
33745         });
33746         this.last = this.navgroup.addItem({
33747             tooltip: this.lastText,
33748             html : ' <i class="fa fa-step-forward"></i>',
33749             cls: "next btn-outline-secondary",
33750             disabled: true,
33751             preventDefault: true,
33752             listeners : { click :  this.onClick.createDelegate(this, ["last"]) }
33753         });
33754     //this.addSeparator();
33755         this.loading = this.navgroup.addItem({
33756             tooltip: this.refreshText,
33757             cls: "btn-outline-secondary",
33758             html : ' <i class="fa fa-refresh"></i>',
33759             preventDefault: true,
33760             listeners : { click : this.onClick.createDelegate(this, ["refresh"]) }
33761         });
33762         
33763     },
33764
33765     // private
33766     updateInfo : function(){
33767         if(this.displayEl){
33768             var count = (typeof(this.getCount) == 'undefined') ? this.ds.getCount() : this.getCount();
33769             var msg = count == 0 ?
33770                 this.emptyMsg :
33771                 String.format(
33772                     this.displayMsg,
33773                     this.cursor+1, this.cursor+count, this.ds.getTotalCount()    
33774                 );
33775             this.displayEl.update(msg);
33776         }
33777     },
33778
33779     // private
33780     onLoad : function(ds, r, o)
33781     {
33782         this.cursor = o.params && o.params.start ? o.params.start : 0;
33783         
33784         var d = this.getPageData(),
33785             ap = d.activePage,
33786             ps = d.pages;
33787         
33788         
33789         this.afterTextEl.dom.innerHTML = String.format(this.afterPageText, d.pages);
33790         this.field.dom.value = ap;
33791         this.first.setDisabled(ap == 1);
33792         this.prev.setDisabled(ap == 1);
33793         this.next.setDisabled(ap == ps);
33794         this.last.setDisabled(ap == ps);
33795         this.loading.enable();
33796         this.updateInfo();
33797     },
33798
33799     // private
33800     getPageData : function(){
33801         var total = this.ds.getTotalCount();
33802         return {
33803             total : total,
33804             activePage : Math.ceil((this.cursor+this.pageSize)/this.pageSize),
33805             pages :  total < this.pageSize ? 1 : Math.ceil(total/this.pageSize)
33806         };
33807     },
33808
33809     // private
33810     onLoadError : function(proxy, o){
33811         this.loading.enable();
33812         if (this.ds.events.loadexception.listeners.length  < 2) {
33813             // nothing has been assigned to loadexception except this...
33814             // so 
33815             Roo.MessageBox.alert("Error loading",o.raw.errorMsg);
33816
33817         }
33818     },
33819
33820     // private
33821     onPagingKeydown : function(e){
33822         var k = e.getKey();
33823         var d = this.getPageData();
33824         if(k == e.RETURN){
33825             var v = this.field.dom.value, pageNum;
33826             if(!v || isNaN(pageNum = parseInt(v, 10))){
33827                 this.field.dom.value = d.activePage;
33828                 return;
33829             }
33830             pageNum = Math.min(Math.max(1, pageNum), d.pages) - 1;
33831             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
33832             e.stopEvent();
33833         }
33834         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))
33835         {
33836           var pageNum = (k == e.HOME || (k == e.DOWN && e.ctrlKey) || (k == e.LEFT && e.ctrlKey) || (k == e.PAGEDOWN && e.ctrlKey)) ? 1 : d.pages;
33837           this.field.dom.value = pageNum;
33838           this.ds.load({params:{start: (pageNum - 1) * this.pageSize, limit: this.pageSize}});
33839           e.stopEvent();
33840         }
33841         else if(k == e.UP || k == e.RIGHT || k == e.PAGEUP || k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
33842         {
33843           var v = this.field.dom.value, pageNum; 
33844           var increment = (e.shiftKey) ? 10 : 1;
33845           if(k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN) {
33846                 increment *= -1;
33847           }
33848           if(!v || isNaN(pageNum = parseInt(v, 10))) {
33849             this.field.dom.value = d.activePage;
33850             return;
33851           }
33852           else if(parseInt(v, 10) + increment >= 1 & parseInt(v, 10) + increment <= d.pages)
33853           {
33854             this.field.dom.value = parseInt(v, 10) + increment;
33855             pageNum = Math.min(Math.max(1, pageNum + increment), d.pages) - 1;
33856             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
33857           }
33858           e.stopEvent();
33859         }
33860     },
33861
33862     // private
33863     beforeLoad : function(){
33864         if(this.loading){
33865             this.loading.disable();
33866         }
33867     },
33868
33869     // private
33870     onClick : function(which){
33871         
33872         var ds = this.ds;
33873         if (!ds) {
33874             return;
33875         }
33876         
33877         switch(which){
33878             case "first":
33879                 ds.load({params:{start: 0, limit: this.pageSize}});
33880             break;
33881             case "prev":
33882                 ds.load({params:{start: Math.max(0, this.cursor-this.pageSize), limit: this.pageSize}});
33883             break;
33884             case "next":
33885                 ds.load({params:{start: this.cursor+this.pageSize, limit: this.pageSize}});
33886             break;
33887             case "last":
33888                 var total = ds.getTotalCount();
33889                 var extra = total % this.pageSize;
33890                 var lastStart = extra ? (total - extra) : total-this.pageSize;
33891                 ds.load({params:{start: lastStart, limit: this.pageSize}});
33892             break;
33893             case "refresh":
33894                 ds.load({params:{start: this.cursor, limit: this.pageSize}});
33895             break;
33896         }
33897     },
33898
33899     /**
33900      * Unbinds the paging toolbar from the specified {@link Roo.data.Store}
33901      * @param {Roo.data.Store} store The data store to unbind
33902      */
33903     unbind : function(ds){
33904         ds.un("beforeload", this.beforeLoad, this);
33905         ds.un("load", this.onLoad, this);
33906         ds.un("loadexception", this.onLoadError, this);
33907         ds.un("remove", this.updateInfo, this);
33908         ds.un("add", this.updateInfo, this);
33909         this.ds = undefined;
33910     },
33911
33912     /**
33913      * Binds the paging toolbar to the specified {@link Roo.data.Store}
33914      * @param {Roo.data.Store} store The data store to bind
33915      */
33916     bind : function(ds){
33917         ds.on("beforeload", this.beforeLoad, this);
33918         ds.on("load", this.onLoad, this);
33919         ds.on("loadexception", this.onLoadError, this);
33920         ds.on("remove", this.updateInfo, this);
33921         ds.on("add", this.updateInfo, this);
33922         this.ds = ds;
33923     }
33924 });/*
33925  * - LGPL
33926  *
33927  * element
33928  * 
33929  */
33930
33931 /**
33932  * @class Roo.bootstrap.MessageBar
33933  * @extends Roo.bootstrap.Component
33934  * Bootstrap MessageBar class
33935  * @cfg {String} html contents of the MessageBar
33936  * @cfg {String} weight (info | success | warning | danger) default info
33937  * @cfg {String} beforeClass insert the bar before the given class
33938  * @cfg {Boolean} closable (true | false) default false
33939  * @cfg {Boolean} fixed (true | false) default false, fix the bar at the top
33940  * 
33941  * @constructor
33942  * Create a new Element
33943  * @param {Object} config The config object
33944  */
33945
33946 Roo.bootstrap.MessageBar = function(config){
33947     Roo.bootstrap.MessageBar.superclass.constructor.call(this, config);
33948 };
33949
33950 Roo.extend(Roo.bootstrap.MessageBar, Roo.bootstrap.Component,  {
33951     
33952     html: '',
33953     weight: 'info',
33954     closable: false,
33955     fixed: false,
33956     beforeClass: 'bootstrap-sticky-wrap',
33957     
33958     getAutoCreate : function(){
33959         
33960         var cfg = {
33961             tag: 'div',
33962             cls: 'alert alert-dismissable alert-' + this.weight,
33963             cn: [
33964                 {
33965                     tag: 'span',
33966                     cls: 'message',
33967                     html: this.html || ''
33968                 }
33969             ]
33970         };
33971         
33972         if(this.fixed){
33973             cfg.cls += ' alert-messages-fixed';
33974         }
33975         
33976         if(this.closable){
33977             cfg.cn.push({
33978                 tag: 'button',
33979                 cls: 'close',
33980                 html: 'x'
33981             });
33982         }
33983         
33984         return cfg;
33985     },
33986     
33987     onRender : function(ct, position)
33988     {
33989         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
33990         
33991         if(!this.el){
33992             var cfg = Roo.apply({},  this.getAutoCreate());
33993             cfg.id = Roo.id();
33994             
33995             if (this.cls) {
33996                 cfg.cls += ' ' + this.cls;
33997             }
33998             if (this.style) {
33999                 cfg.style = this.style;
34000             }
34001             this.el = Roo.get(document.body).createChild(cfg, Roo.select('.'+this.beforeClass, true).first());
34002             
34003             this.el.setVisibilityMode(Roo.Element.DISPLAY);
34004         }
34005         
34006         this.el.select('>button.close').on('click', this.hide, this);
34007         
34008     },
34009     
34010     show : function()
34011     {
34012         if (!this.rendered) {
34013             this.render();
34014         }
34015         
34016         this.el.show();
34017         
34018         this.fireEvent('show', this);
34019         
34020     },
34021     
34022     hide : function()
34023     {
34024         if (!this.rendered) {
34025             this.render();
34026         }
34027         
34028         this.el.hide();
34029         
34030         this.fireEvent('hide', this);
34031     },
34032     
34033     update : function()
34034     {
34035 //        var e = this.el.dom.firstChild;
34036 //        
34037 //        if(this.closable){
34038 //            e = e.nextSibling;
34039 //        }
34040 //        
34041 //        e.data = this.html || '';
34042
34043         this.el.select('>.message', true).first().dom.innerHTML = this.html || '';
34044     }
34045    
34046 });
34047
34048  
34049
34050      /*
34051  * - LGPL
34052  *
34053  * Graph
34054  * 
34055  */
34056
34057
34058 /**
34059  * @class Roo.bootstrap.Graph
34060  * @extends Roo.bootstrap.Component
34061  * Bootstrap Graph class
34062 > Prameters
34063  -sm {number} sm 4
34064  -md {number} md 5
34065  @cfg {String} graphtype  bar | vbar | pie
34066  @cfg {number} g_x coodinator | centre x (pie)
34067  @cfg {number} g_y coodinator | centre y (pie)
34068  @cfg {number} g_r radius (pie)
34069  @cfg {number} g_height height of the chart (respected by all elements in the set)
34070  @cfg {number} g_width width of the chart (respected by all elements in the set)
34071  @cfg {Object} title The title of the chart
34072     
34073  -{Array}  values
34074  -opts (object) options for the chart 
34075      o {
34076      o type (string) type of endings of the bar. Default: 'square'. Other options are: 'round', 'sharp', 'soft'.
34077      o gutter (number)(string) default '20%' (WHAT DOES IT DO?)
34078      o vgutter (number)
34079      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.
34080      o stacked (boolean) whether or not to tread values as in a stacked bar chart
34081      o to
34082      o stretch (boolean)
34083      o }
34084  -opts (object) options for the pie
34085      o{
34086      o cut
34087      o startAngle (number)
34088      o endAngle (number)
34089      } 
34090  *
34091  * @constructor
34092  * Create a new Input
34093  * @param {Object} config The config object
34094  */
34095
34096 Roo.bootstrap.Graph = function(config){
34097     Roo.bootstrap.Graph.superclass.constructor.call(this, config);
34098     
34099     this.addEvents({
34100         // img events
34101         /**
34102          * @event click
34103          * The img click event for the img.
34104          * @param {Roo.EventObject} e
34105          */
34106         "click" : true
34107     });
34108 };
34109
34110 Roo.extend(Roo.bootstrap.Graph, Roo.bootstrap.Component,  {
34111     
34112     sm: 4,
34113     md: 5,
34114     graphtype: 'bar',
34115     g_height: 250,
34116     g_width: 400,
34117     g_x: 50,
34118     g_y: 50,
34119     g_r: 30,
34120     opts:{
34121         //g_colors: this.colors,
34122         g_type: 'soft',
34123         g_gutter: '20%'
34124
34125     },
34126     title : false,
34127
34128     getAutoCreate : function(){
34129         
34130         var cfg = {
34131             tag: 'div',
34132             html : null
34133         };
34134         
34135         
34136         return  cfg;
34137     },
34138
34139     onRender : function(ct,position){
34140         
34141         
34142         Roo.bootstrap.Graph.superclass.onRender.call(this,ct,position);
34143         
34144         if (typeof(Raphael) == 'undefined') {
34145             Roo.bootstrap.MessageBox.alert("Error","Raphael is not availabe");
34146             return;
34147         }
34148         
34149         this.raphael = Raphael(this.el.dom);
34150         
34151                     // data1 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
34152                     // data2 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
34153                     // data3 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
34154                     // txtattr = { font: "12px 'Fontin Sans', Fontin-Sans, sans-serif" };
34155                 /*
34156                 r.text(160, 10, "Single Series Chart").attr(txtattr);
34157                 r.text(480, 10, "Multiline Series Chart").attr(txtattr);
34158                 r.text(160, 250, "Multiple Series Stacked Chart").attr(txtattr);
34159                 r.text(480, 250, 'Multiline Series Stacked Vertical Chart. Type "round"').attr(txtattr);
34160                 
34161                 r.barchart(10, 10, 300, 220, [[55, 20, 13, 32, 5, 1, 2, 10]], 0, {type: "sharp"});
34162                 r.barchart(330, 10, 300, 220, data1);
34163                 r.barchart(10, 250, 300, 220, data2, {stacked: true});
34164                 r.barchart(330, 250, 300, 220, data3, {stacked: true, type: "round"});
34165                 */
34166                 
34167                 // var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
34168                 // r.barchart(30, 30, 560, 250,  xdata, {
34169                 //    labels : [55, 20, 13, 32, 5, 1, 2, 10,5 , 10],
34170                 //     axis : "0 0 1 1",
34171                 //     axisxlabels :  xdata
34172                 //     //yvalues : cols,
34173                    
34174                 // });
34175 //        var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
34176 //        
34177 //        this.load(null,xdata,{
34178 //                axis : "0 0 1 1",
34179 //                axisxlabels :  xdata
34180 //                });
34181
34182     },
34183
34184     load : function(graphtype,xdata,opts)
34185     {
34186         this.raphael.clear();
34187         if(!graphtype) {
34188             graphtype = this.graphtype;
34189         }
34190         if(!opts){
34191             opts = this.opts;
34192         }
34193         var r = this.raphael,
34194             fin = function () {
34195                 this.flag = r.popup(this.bar.x, this.bar.y, this.bar.value || "0").insertBefore(this);
34196             },
34197             fout = function () {
34198                 this.flag.animate({opacity: 0}, 300, function () {this.remove();});
34199             },
34200             pfin = function() {
34201                 this.sector.stop();
34202                 this.sector.scale(1.1, 1.1, this.cx, this.cy);
34203
34204                 if (this.label) {
34205                     this.label[0].stop();
34206                     this.label[0].attr({ r: 7.5 });
34207                     this.label[1].attr({ "font-weight": 800 });
34208                 }
34209             },
34210             pfout = function() {
34211                 this.sector.animate({ transform: 's1 1 ' + this.cx + ' ' + this.cy }, 500, "bounce");
34212
34213                 if (this.label) {
34214                     this.label[0].animate({ r: 5 }, 500, "bounce");
34215                     this.label[1].attr({ "font-weight": 400 });
34216                 }
34217             };
34218
34219         switch(graphtype){
34220             case 'bar':
34221                 this.raphael.barchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
34222                 break;
34223             case 'hbar':
34224                 this.raphael.hbarchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
34225                 break;
34226             case 'pie':
34227 //                opts = { legend: ["%% - Enterprise Users", "% - ddd","Chrome Users"], legendpos: "west", 
34228 //                href: ["http://raphaeljs.com", "http://g.raphaeljs.com"]};
34229 //            
34230                 this.raphael.piechart(this.g_x,this.g_y,this.g_r,xdata,opts).hover(pfin, pfout);
34231                 
34232                 break;
34233
34234         }
34235         
34236         if(this.title){
34237             this.raphael.text(this.title.x, this.title.y, this.title.text).attr(this.title.attr);
34238         }
34239         
34240     },
34241     
34242     setTitle: function(o)
34243     {
34244         this.title = o;
34245     },
34246     
34247     initEvents: function() {
34248         
34249         if(!this.href){
34250             this.el.on('click', this.onClick, this);
34251         }
34252     },
34253     
34254     onClick : function(e)
34255     {
34256         Roo.log('img onclick');
34257         this.fireEvent('click', this, e);
34258     }
34259    
34260 });
34261
34262  
34263 Roo.bootstrap.dash = {};/*
34264  * - LGPL
34265  *
34266  * numberBox
34267  * 
34268  */
34269 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
34270
34271 /**
34272  * @class Roo.bootstrap.dash.NumberBox
34273  * @extends Roo.bootstrap.Component
34274  * Bootstrap NumberBox class
34275  * @cfg {String} headline Box headline
34276  * @cfg {String} content Box content
34277  * @cfg {String} icon Box icon
34278  * @cfg {String} footer Footer text
34279  * @cfg {String} fhref Footer href
34280  * 
34281  * @constructor
34282  * Create a new NumberBox
34283  * @param {Object} config The config object
34284  */
34285
34286
34287 Roo.bootstrap.dash.NumberBox = function(config){
34288     Roo.bootstrap.dash.NumberBox.superclass.constructor.call(this, config);
34289     
34290 };
34291
34292 Roo.extend(Roo.bootstrap.dash.NumberBox, Roo.bootstrap.Component,  {
34293     
34294     headline : '',
34295     content : '',
34296     icon : '',
34297     footer : '',
34298     fhref : '',
34299     ficon : '',
34300     
34301     getAutoCreate : function(){
34302         
34303         var cfg = {
34304             tag : 'div',
34305             cls : 'small-box ',
34306             cn : [
34307                 {
34308                     tag : 'div',
34309                     cls : 'inner',
34310                     cn :[
34311                         {
34312                             tag : 'h3',
34313                             cls : 'roo-headline',
34314                             html : this.headline
34315                         },
34316                         {
34317                             tag : 'p',
34318                             cls : 'roo-content',
34319                             html : this.content
34320                         }
34321                     ]
34322                 }
34323             ]
34324         };
34325         
34326         if(this.icon){
34327             cfg.cn.push({
34328                 tag : 'div',
34329                 cls : 'icon',
34330                 cn :[
34331                     {
34332                         tag : 'i',
34333                         cls : 'ion ' + this.icon
34334                     }
34335                 ]
34336             });
34337         }
34338         
34339         if(this.footer){
34340             var footer = {
34341                 tag : 'a',
34342                 cls : 'small-box-footer',
34343                 href : this.fhref || '#',
34344                 html : this.footer
34345             };
34346             
34347             cfg.cn.push(footer);
34348             
34349         }
34350         
34351         return  cfg;
34352     },
34353
34354     onRender : function(ct,position){
34355         Roo.bootstrap.dash.NumberBox.superclass.onRender.call(this,ct,position);
34356
34357
34358        
34359                 
34360     },
34361
34362     setHeadline: function (value)
34363     {
34364         this.el.select('.roo-headline',true).first().dom.innerHTML = value;
34365     },
34366     
34367     setFooter: function (value, href)
34368     {
34369         this.el.select('a.small-box-footer',true).first().dom.innerHTML = value;
34370         
34371         if(href){
34372             this.el.select('a.small-box-footer',true).first().attr('href', href);
34373         }
34374         
34375     },
34376
34377     setContent: function (value)
34378     {
34379         this.el.select('.roo-content',true).first().dom.innerHTML = value;
34380     },
34381
34382     initEvents: function() 
34383     {   
34384         
34385     }
34386     
34387 });
34388
34389  
34390 /*
34391  * - LGPL
34392  *
34393  * TabBox
34394  * 
34395  */
34396 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
34397
34398 /**
34399  * @class Roo.bootstrap.dash.TabBox
34400  * @extends Roo.bootstrap.Component
34401  * @children Roo.bootstrap.dash.TabPane
34402  * Bootstrap TabBox class
34403  * @cfg {String} title Title of the TabBox
34404  * @cfg {String} icon Icon of the TabBox
34405  * @cfg {Boolean} showtabs (true|false) show the tabs default true
34406  * @cfg {Boolean} tabScrollable (true|false) tab scrollable when mobile view default false
34407  * 
34408  * @constructor
34409  * Create a new TabBox
34410  * @param {Object} config The config object
34411  */
34412
34413
34414 Roo.bootstrap.dash.TabBox = function(config){
34415     Roo.bootstrap.dash.TabBox.superclass.constructor.call(this, config);
34416     this.addEvents({
34417         // raw events
34418         /**
34419          * @event addpane
34420          * When a pane is added
34421          * @param {Roo.bootstrap.dash.TabPane} pane
34422          */
34423         "addpane" : true,
34424         /**
34425          * @event activatepane
34426          * When a pane is activated
34427          * @param {Roo.bootstrap.dash.TabPane} pane
34428          */
34429         "activatepane" : true
34430         
34431          
34432     });
34433     
34434     this.panes = [];
34435 };
34436
34437 Roo.extend(Roo.bootstrap.dash.TabBox, Roo.bootstrap.Component,  {
34438
34439     title : '',
34440     icon : false,
34441     showtabs : true,
34442     tabScrollable : false,
34443     
34444     getChildContainer : function()
34445     {
34446         return this.el.select('.tab-content', true).first();
34447     },
34448     
34449     getAutoCreate : function(){
34450         
34451         var header = {
34452             tag: 'li',
34453             cls: 'pull-left header',
34454             html: this.title,
34455             cn : []
34456         };
34457         
34458         if(this.icon){
34459             header.cn.push({
34460                 tag: 'i',
34461                 cls: 'fa ' + this.icon
34462             });
34463         }
34464         
34465         var h = {
34466             tag: 'ul',
34467             cls: 'nav nav-tabs pull-right',
34468             cn: [
34469                 header
34470             ]
34471         };
34472         
34473         if(this.tabScrollable){
34474             h = {
34475                 tag: 'div',
34476                 cls: 'tab-header',
34477                 cn: [
34478                     {
34479                         tag: 'ul',
34480                         cls: 'nav nav-tabs pull-right',
34481                         cn: [
34482                             header
34483                         ]
34484                     }
34485                 ]
34486             };
34487         }
34488         
34489         var cfg = {
34490             tag: 'div',
34491             cls: 'nav-tabs-custom',
34492             cn: [
34493                 h,
34494                 {
34495                     tag: 'div',
34496                     cls: 'tab-content no-padding',
34497                     cn: []
34498                 }
34499             ]
34500         };
34501
34502         return  cfg;
34503     },
34504     initEvents : function()
34505     {
34506         //Roo.log('add add pane handler');
34507         this.on('addpane', this.onAddPane, this);
34508     },
34509      /**
34510      * Updates the box title
34511      * @param {String} html to set the title to.
34512      */
34513     setTitle : function(value)
34514     {
34515         this.el.select('.nav-tabs .header', true).first().dom.innerHTML = value;
34516     },
34517     onAddPane : function(pane)
34518     {
34519         this.panes.push(pane);
34520         //Roo.log('addpane');
34521         //Roo.log(pane);
34522         // tabs are rendere left to right..
34523         if(!this.showtabs){
34524             return;
34525         }
34526         
34527         var ctr = this.el.select('.nav-tabs', true).first();
34528          
34529          
34530         var existing = ctr.select('.nav-tab',true);
34531         var qty = existing.getCount();;
34532         
34533         
34534         var tab = ctr.createChild({
34535             tag : 'li',
34536             cls : 'nav-tab' + (qty ? '' : ' active'),
34537             cn : [
34538                 {
34539                     tag : 'a',
34540                     href:'#',
34541                     html : pane.title
34542                 }
34543             ]
34544         }, qty ? existing.first().dom : ctr.select('.header', true).first().dom );
34545         pane.tab = tab;
34546         
34547         tab.on('click', this.onTabClick.createDelegate(this, [pane], true));
34548         if (!qty) {
34549             pane.el.addClass('active');
34550         }
34551         
34552                 
34553     },
34554     onTabClick : function(ev,un,ob,pane)
34555     {
34556         //Roo.log('tab - prev default');
34557         ev.preventDefault();
34558         
34559         
34560         this.el.select('.nav-tabs li.nav-tab', true).removeClass('active');
34561         pane.tab.addClass('active');
34562         //Roo.log(pane.title);
34563         this.getChildContainer().select('.tab-pane',true).removeClass('active');
34564         // technically we should have a deactivate event.. but maybe add later.
34565         // and it should not de-activate the selected tab...
34566         this.fireEvent('activatepane', pane);
34567         pane.el.addClass('active');
34568         pane.fireEvent('activate');
34569         
34570         
34571     },
34572     
34573     getActivePane : function()
34574     {
34575         var r = false;
34576         Roo.each(this.panes, function(p) {
34577             if(p.el.hasClass('active')){
34578                 r = p;
34579                 return false;
34580             }
34581             
34582             return;
34583         });
34584         
34585         return r;
34586     }
34587     
34588     
34589 });
34590
34591  
34592 /*
34593  * - LGPL
34594  *
34595  * Tab pane
34596  * 
34597  */
34598 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
34599 /**
34600  * @class Roo.bootstrap.TabPane
34601  * @extends Roo.bootstrap.Component
34602  * @children  Roo.bootstrap.Graph Roo.bootstrap.Column
34603  * Bootstrap TabPane class
34604  * @cfg {Boolean} active (false | true) Default false
34605  * @cfg {String} title title of panel
34606
34607  * 
34608  * @constructor
34609  * Create a new TabPane
34610  * @param {Object} config The config object
34611  */
34612
34613 Roo.bootstrap.dash.TabPane = function(config){
34614     Roo.bootstrap.dash.TabPane.superclass.constructor.call(this, config);
34615     
34616     this.addEvents({
34617         // raw events
34618         /**
34619          * @event activate
34620          * When a pane is activated
34621          * @param {Roo.bootstrap.dash.TabPane} pane
34622          */
34623         "activate" : true
34624          
34625     });
34626 };
34627
34628 Roo.extend(Roo.bootstrap.dash.TabPane, Roo.bootstrap.Component,  {
34629     
34630     active : false,
34631     title : '',
34632     
34633     // the tabBox that this is attached to.
34634     tab : false,
34635      
34636     getAutoCreate : function() 
34637     {
34638         var cfg = {
34639             tag: 'div',
34640             cls: 'tab-pane'
34641         };
34642         
34643         if(this.active){
34644             cfg.cls += ' active';
34645         }
34646         
34647         return cfg;
34648     },
34649     initEvents  : function()
34650     {
34651         //Roo.log('trigger add pane handler');
34652         this.parent().fireEvent('addpane', this)
34653     },
34654     
34655      /**
34656      * Updates the tab title 
34657      * @param {String} html to set the title to.
34658      */
34659     setTitle: function(str)
34660     {
34661         if (!this.tab) {
34662             return;
34663         }
34664         this.title = str;
34665         this.tab.select('a', true).first().dom.innerHTML = str;
34666         
34667     }
34668     
34669     
34670     
34671 });
34672
34673  
34674
34675
34676  /*
34677  * - LGPL
34678  *
34679  * Tooltip
34680  * 
34681  */
34682
34683 /**
34684  * @class Roo.bootstrap.Tooltip
34685  * Bootstrap Tooltip class
34686  * This is basic at present - all componets support it by default, however they should add tooltipEl() method
34687  * to determine which dom element triggers the tooltip.
34688  * 
34689  * It needs to add support for additional attributes like tooltip-position
34690  * 
34691  * @constructor
34692  * Create a new Toolti
34693  * @param {Object} config The config object
34694  */
34695
34696 Roo.bootstrap.Tooltip = function(config){
34697     Roo.bootstrap.Tooltip.superclass.constructor.call(this, config);
34698     
34699     this.alignment = Roo.bootstrap.Tooltip.alignment;
34700     
34701     if(typeof(config) != 'undefined' && typeof(config.alignment) != 'undefined'){
34702         this.alignment = config.alignment;
34703     }
34704     
34705 };
34706
34707 Roo.apply(Roo.bootstrap.Tooltip, {
34708     /**
34709      * @function init initialize tooltip monitoring.
34710      * @static
34711      */
34712     currentEl : false,
34713     currentTip : false,
34714     currentRegion : false,
34715     
34716     //  init : delay?
34717     
34718     init : function()
34719     {
34720         Roo.get(document).on('mouseover', this.enter ,this);
34721         Roo.get(document).on('mouseout', this.leave, this);
34722          
34723         
34724         this.currentTip = new Roo.bootstrap.Tooltip();
34725     },
34726     
34727     enter : function(ev)
34728     {
34729         var dom = ev.getTarget();
34730         
34731         //Roo.log(['enter',dom]);
34732         var el = Roo.fly(dom);
34733         if (this.currentEl) {
34734             //Roo.log(dom);
34735             //Roo.log(this.currentEl);
34736             //Roo.log(this.currentEl.contains(dom));
34737             if (this.currentEl == el) {
34738                 return;
34739             }
34740             if (dom != this.currentEl.dom && this.currentEl.contains(dom)) {
34741                 return;
34742             }
34743
34744         }
34745         
34746         if (this.currentTip.el) {
34747             this.currentTip.el.setVisibilityMode(Roo.Element.DISPLAY).hide(); // force hiding...
34748         }    
34749         //Roo.log(ev);
34750         
34751         if(!el || el.dom == document){
34752             return;
34753         }
34754         
34755         var bindEl = el; 
34756         var pel = false;
34757         if (!el.attr('tooltip')) {
34758             pel = el.findParent("[tooltip]");
34759             if (pel) {
34760                 bindEl = Roo.get(pel);
34761             }
34762         }
34763         
34764        
34765         
34766         // you can not look for children, as if el is the body.. then everythign is the child..
34767         if (!pel && !el.attr('tooltip')) { //
34768             if (!el.select("[tooltip]").elements.length) {
34769                 return;
34770             }
34771             // is the mouse over this child...?
34772             bindEl = el.select("[tooltip]").first();
34773             var xy = ev.getXY();
34774             if (!bindEl.getRegion().contains( { top : xy[1] ,right : xy[0] , bottom : xy[1], left : xy[0]})) {
34775                 //Roo.log("not in region.");
34776                 return;
34777             }
34778             //Roo.log("child element over..");
34779             
34780         }
34781         this.currentEl = el;
34782         this.currentTip.bind(bindEl);
34783         this.currentRegion = Roo.lib.Region.getRegion(dom);
34784         this.currentTip.enter();
34785         
34786     },
34787     leave : function(ev)
34788     {
34789         var dom = ev.getTarget();
34790         //Roo.log(['leave',dom]);
34791         if (!this.currentEl) {
34792             return;
34793         }
34794         
34795         
34796         if (dom != this.currentEl.dom) {
34797             return;
34798         }
34799         var xy = ev.getXY();
34800         if (this.currentRegion.contains( new Roo.lib.Region( xy[1], xy[0] ,xy[1], xy[0]  ))) {
34801             return;
34802         }
34803         // only activate leave if mouse cursor is outside... bounding box..
34804         
34805         
34806         
34807         
34808         if (this.currentTip) {
34809             this.currentTip.leave();
34810         }
34811         //Roo.log('clear currentEl');
34812         this.currentEl = false;
34813         
34814         
34815     },
34816     alignment : {
34817         'left' : ['r-l', [-2,0], 'right'],
34818         'right' : ['l-r', [2,0], 'left'],
34819         'bottom' : ['t-b', [0,2], 'top'],
34820         'top' : [ 'b-t', [0,-2], 'bottom']
34821     }
34822     
34823 });
34824
34825
34826 Roo.extend(Roo.bootstrap.Tooltip, Roo.bootstrap.Component,  {
34827     
34828     
34829     bindEl : false,
34830     
34831     delay : null, // can be { show : 300 , hide: 500}
34832     
34833     timeout : null,
34834     
34835     hoverState : null, //???
34836     
34837     placement : 'bottom', 
34838     
34839     alignment : false,
34840     
34841     getAutoCreate : function(){
34842     
34843         var cfg = {
34844            cls : 'tooltip',   
34845            role : 'tooltip',
34846            cn : [
34847                 {
34848                     cls : 'tooltip-arrow arrow'
34849                 },
34850                 {
34851                     cls : 'tooltip-inner'
34852                 }
34853            ]
34854         };
34855         
34856         return cfg;
34857     },
34858     bind : function(el)
34859     {
34860         this.bindEl = el;
34861     },
34862     
34863     initEvents : function()
34864     {
34865         this.arrowEl = this.el.select('.arrow', true).first();
34866         this.innerEl = this.el.select('.tooltip-inner', true).first();
34867     },
34868     
34869     enter : function () {
34870        
34871         if (this.timeout != null) {
34872             clearTimeout(this.timeout);
34873         }
34874         
34875         this.hoverState = 'in';
34876          //Roo.log("enter - show");
34877         if (!this.delay || !this.delay.show) {
34878             this.show();
34879             return;
34880         }
34881         var _t = this;
34882         this.timeout = setTimeout(function () {
34883             if (_t.hoverState == 'in') {
34884                 _t.show();
34885             }
34886         }, this.delay.show);
34887     },
34888     leave : function()
34889     {
34890         clearTimeout(this.timeout);
34891     
34892         this.hoverState = 'out';
34893          if (!this.delay || !this.delay.hide) {
34894             this.hide();
34895             return;
34896         }
34897        
34898         var _t = this;
34899         this.timeout = setTimeout(function () {
34900             //Roo.log("leave - timeout");
34901             
34902             if (_t.hoverState == 'out') {
34903                 _t.hide();
34904                 Roo.bootstrap.Tooltip.currentEl = false;
34905             }
34906         }, delay);
34907     },
34908     
34909     show : function (msg)
34910     {
34911         if (!this.el) {
34912             this.render(document.body);
34913         }
34914         // set content.
34915         //Roo.log([this.bindEl, this.bindEl.attr('tooltip')]);
34916         
34917         var tip = msg || this.bindEl.attr('tooltip') || this.bindEl.select("[tooltip]").first().attr('tooltip');
34918         
34919         this.el.select('.tooltip-inner',true).first().dom.innerHTML = tip;
34920         
34921         this.el.removeClass(['fade','top','bottom', 'left', 'right','in',
34922                              'bs-tooltip-top','bs-tooltip-bottom', 'bs-tooltip-left', 'bs-tooltip-right']);
34923
34924         if(this.bindEl.attr('tooltip-class')) {
34925             this.el.addClass(this.bindEl.attr('tooltip-class'));
34926         }
34927         
34928         var placement = typeof this.placement == 'function' ?
34929             this.placement.call(this, this.el, on_el) :
34930             this.placement;
34931         
34932         if(this.bindEl.attr('tooltip-placement')) {
34933             placement = this.bindEl.attr('tooltip-placement');
34934         }
34935             
34936         var autoToken = /\s?auto?\s?/i;
34937         var autoPlace = autoToken.test(placement);
34938         if (autoPlace) {
34939             placement = placement.replace(autoToken, '') || 'top';
34940         }
34941         
34942         //this.el.detach()
34943         //this.el.setXY([0,0]);
34944         this.el.show();
34945         //this.el.dom.style.display='block';
34946         
34947         //this.el.appendTo(on_el);
34948         
34949         var p = this.getPosition();
34950         var box = this.el.getBox();
34951         
34952         if (autoPlace) {
34953             // fixme..
34954         }
34955         
34956         var align = this.alignment[placement];
34957         
34958         var xy = this.el.getAlignToXY(this.bindEl, align[0], align[1]);
34959         
34960         if(placement == 'top' || placement == 'bottom'){
34961             if(xy[0] < 0){
34962                 placement = 'right';
34963             }
34964             
34965             if(xy[0] + this.el.getWidth() > Roo.lib.Dom.getViewWidth()){
34966                 placement = 'left';
34967             }
34968             
34969             var scroll = Roo.select('body', true).first().getScroll();
34970             
34971             if(xy[1] > Roo.lib.Dom.getViewHeight() + scroll.top - this.el.getHeight()){
34972                 placement = 'top';
34973             }
34974             
34975             align = this.alignment[placement];
34976             
34977             this.arrowEl.setLeft((this.innerEl.getWidth()/2) - 5);
34978             
34979         }
34980         
34981         var elems = document.getElementsByTagName('div');
34982         var highest = Number.MIN_SAFE_INTEGER || -(Math.pow(2, 53) - 1);
34983         for (var i = 0; i < elems.length; i++) {
34984           var zindex = Number.parseInt(
34985                 document.defaultView.getComputedStyle(elems[i], null).getPropertyValue("z-index"),
34986                 10
34987           );
34988           if (zindex > highest) {
34989             highest = zindex;
34990           }
34991         }
34992         
34993         
34994         
34995         this.el.dom.style.zIndex = highest;
34996         
34997         this.el.alignTo(this.bindEl, align[0],align[1]);
34998         //var arrow = this.el.select('.arrow',true).first();
34999         //arrow.set(align[2], 
35000         
35001         this.el.addClass(placement);
35002         this.el.addClass("bs-tooltip-"+ placement);
35003         
35004         this.el.addClass('in fade show');
35005         
35006         this.hoverState = null;
35007         
35008         if (this.el.hasClass('fade')) {
35009             // fade it?
35010         }
35011         
35012         
35013         
35014         
35015         
35016     },
35017     hide : function()
35018     {
35019          
35020         if (!this.el) {
35021             return;
35022         }
35023         //this.el.setXY([0,0]);
35024         if(this.bindEl.attr('tooltip-class')) {
35025             this.el.removeClass(this.bindEl.attr('tooltip-class'));
35026         }
35027         this.el.removeClass(['show', 'in']);
35028         //this.el.hide();
35029         
35030     }
35031     
35032 });
35033  
35034
35035  /*
35036  * - LGPL
35037  *
35038  * Location Picker
35039  * 
35040  */
35041
35042 /**
35043  * @class Roo.bootstrap.LocationPicker
35044  * @extends Roo.bootstrap.Component
35045  * Bootstrap LocationPicker class
35046  * @cfg {Number} latitude Position when init default 0
35047  * @cfg {Number} longitude Position when init default 0
35048  * @cfg {Number} zoom default 15
35049  * @cfg {String} mapTypeId default google.maps.MapTypeId.ROADMAP
35050  * @cfg {Boolean} mapTypeControl default false
35051  * @cfg {Boolean} disableDoubleClickZoom default false
35052  * @cfg {Boolean} scrollwheel default true
35053  * @cfg {Boolean} streetViewControl default false
35054  * @cfg {Number} radius default 0
35055  * @cfg {String} locationName
35056  * @cfg {Boolean} draggable default true
35057  * @cfg {Boolean} enableAutocomplete default false
35058  * @cfg {Boolean} enableReverseGeocode default true
35059  * @cfg {String} markerTitle
35060  * 
35061  * @constructor
35062  * Create a new LocationPicker
35063  * @param {Object} config The config object
35064  */
35065
35066
35067 Roo.bootstrap.LocationPicker = function(config){
35068     
35069     Roo.bootstrap.LocationPicker.superclass.constructor.call(this, config);
35070     
35071     this.addEvents({
35072         /**
35073          * @event initial
35074          * Fires when the picker initialized.
35075          * @param {Roo.bootstrap.LocationPicker} this
35076          * @param {Google Location} location
35077          */
35078         initial : true,
35079         /**
35080          * @event positionchanged
35081          * Fires when the picker position changed.
35082          * @param {Roo.bootstrap.LocationPicker} this
35083          * @param {Google Location} location
35084          */
35085         positionchanged : true,
35086         /**
35087          * @event resize
35088          * Fires when the map resize.
35089          * @param {Roo.bootstrap.LocationPicker} this
35090          */
35091         resize : true,
35092         /**
35093          * @event show
35094          * Fires when the map show.
35095          * @param {Roo.bootstrap.LocationPicker} this
35096          */
35097         show : true,
35098         /**
35099          * @event hide
35100          * Fires when the map hide.
35101          * @param {Roo.bootstrap.LocationPicker} this
35102          */
35103         hide : true,
35104         /**
35105          * @event mapClick
35106          * Fires when click the map.
35107          * @param {Roo.bootstrap.LocationPicker} this
35108          * @param {Map event} e
35109          */
35110         mapClick : true,
35111         /**
35112          * @event mapRightClick
35113          * Fires when right click the map.
35114          * @param {Roo.bootstrap.LocationPicker} this
35115          * @param {Map event} e
35116          */
35117         mapRightClick : true,
35118         /**
35119          * @event markerClick
35120          * Fires when click the marker.
35121          * @param {Roo.bootstrap.LocationPicker} this
35122          * @param {Map event} e
35123          */
35124         markerClick : true,
35125         /**
35126          * @event markerRightClick
35127          * Fires when right click the marker.
35128          * @param {Roo.bootstrap.LocationPicker} this
35129          * @param {Map event} e
35130          */
35131         markerRightClick : true,
35132         /**
35133          * @event OverlayViewDraw
35134          * Fires when OverlayView Draw
35135          * @param {Roo.bootstrap.LocationPicker} this
35136          */
35137         OverlayViewDraw : true,
35138         /**
35139          * @event OverlayViewOnAdd
35140          * Fires when OverlayView Draw
35141          * @param {Roo.bootstrap.LocationPicker} this
35142          */
35143         OverlayViewOnAdd : true,
35144         /**
35145          * @event OverlayViewOnRemove
35146          * Fires when OverlayView Draw
35147          * @param {Roo.bootstrap.LocationPicker} this
35148          */
35149         OverlayViewOnRemove : true,
35150         /**
35151          * @event OverlayViewShow
35152          * Fires when OverlayView Draw
35153          * @param {Roo.bootstrap.LocationPicker} this
35154          * @param {Pixel} cpx
35155          */
35156         OverlayViewShow : true,
35157         /**
35158          * @event OverlayViewHide
35159          * Fires when OverlayView Draw
35160          * @param {Roo.bootstrap.LocationPicker} this
35161          */
35162         OverlayViewHide : true,
35163         /**
35164          * @event loadexception
35165          * Fires when load google lib failed.
35166          * @param {Roo.bootstrap.LocationPicker} this
35167          */
35168         loadexception : true
35169     });
35170         
35171 };
35172
35173 Roo.extend(Roo.bootstrap.LocationPicker, Roo.bootstrap.Component,  {
35174     
35175     gMapContext: false,
35176     
35177     latitude: 0,
35178     longitude: 0,
35179     zoom: 15,
35180     mapTypeId: false,
35181     mapTypeControl: false,
35182     disableDoubleClickZoom: false,
35183     scrollwheel: true,
35184     streetViewControl: false,
35185     radius: 0,
35186     locationName: '',
35187     draggable: true,
35188     enableAutocomplete: false,
35189     enableReverseGeocode: true,
35190     markerTitle: '',
35191     
35192     getAutoCreate: function()
35193     {
35194
35195         var cfg = {
35196             tag: 'div',
35197             cls: 'roo-location-picker'
35198         };
35199         
35200         return cfg
35201     },
35202     
35203     initEvents: function(ct, position)
35204     {       
35205         if(!this.el.getWidth() || this.isApplied()){
35206             return;
35207         }
35208         
35209         this.el.setVisibilityMode(Roo.Element.DISPLAY);
35210         
35211         this.initial();
35212     },
35213     
35214     initial: function()
35215     {
35216         if(typeof(google) == 'undefined' || typeof(google.maps) == 'undefined'){
35217             this.fireEvent('loadexception', this);
35218             return;
35219         }
35220         
35221         if(!this.mapTypeId){
35222             this.mapTypeId = google.maps.MapTypeId.ROADMAP;
35223         }
35224         
35225         this.gMapContext = this.GMapContext();
35226         
35227         this.initOverlayView();
35228         
35229         this.OverlayView = new Roo.bootstrap.LocationPicker.OverlayView(this.gMapContext.map);
35230         
35231         var _this = this;
35232                 
35233         google.maps.event.addListener(this.gMapContext.marker, "dragend", function(event) {
35234             _this.setPosition(_this.gMapContext.marker.position);
35235         });
35236         
35237         google.maps.event.addListener(this.gMapContext.map, 'click', function(event){
35238             _this.fireEvent('mapClick', this, event);
35239             
35240         });
35241
35242         google.maps.event.addListener(this.gMapContext.map, 'rightclick', function(event){
35243             _this.fireEvent('mapRightClick', this, event);
35244             
35245         });
35246         
35247         google.maps.event.addListener(this.gMapContext.marker, 'click', function(event){
35248             _this.fireEvent('markerClick', this, event);
35249             
35250         });
35251
35252         google.maps.event.addListener(this.gMapContext.marker, 'rightclick', function(event){
35253             _this.fireEvent('markerRightClick', this, event);
35254             
35255         });
35256         
35257         this.setPosition(this.gMapContext.location);
35258         
35259         this.fireEvent('initial', this, this.gMapContext.location);
35260     },
35261     
35262     initOverlayView: function()
35263     {
35264         var _this = this;
35265         
35266         Roo.bootstrap.LocationPicker.OverlayView.prototype = Roo.apply(new google.maps.OverlayView(), {
35267             
35268             draw: function()
35269             {
35270                 _this.fireEvent('OverlayViewDraw', _this);
35271             },
35272             
35273             onAdd: function()
35274             {
35275                 _this.fireEvent('OverlayViewOnAdd', _this);
35276             },
35277             
35278             onRemove: function()
35279             {
35280                 _this.fireEvent('OverlayViewOnRemove', _this);
35281             },
35282             
35283             show: function(cpx)
35284             {
35285                 _this.fireEvent('OverlayViewShow', _this, cpx);
35286             },
35287             
35288             hide: function()
35289             {
35290                 _this.fireEvent('OverlayViewHide', _this);
35291             }
35292             
35293         });
35294     },
35295     
35296     fromLatLngToContainerPixel: function(event)
35297     {
35298         return this.OverlayView.getProjection().fromLatLngToContainerPixel(event.latLng);
35299     },
35300     
35301     isApplied: function() 
35302     {
35303         return this.getGmapContext() == false ? false : true;
35304     },
35305     
35306     getGmapContext: function() 
35307     {
35308         return (typeof(this.gMapContext) == 'undefined') ? false : this.gMapContext;
35309     },
35310     
35311     GMapContext: function() 
35312     {
35313         var position = new google.maps.LatLng(this.latitude, this.longitude);
35314         
35315         var _map = new google.maps.Map(this.el.dom, {
35316             center: position,
35317             zoom: this.zoom,
35318             mapTypeId: this.mapTypeId,
35319             mapTypeControl: this.mapTypeControl,
35320             disableDoubleClickZoom: this.disableDoubleClickZoom,
35321             scrollwheel: this.scrollwheel,
35322             streetViewControl: this.streetViewControl,
35323             locationName: this.locationName,
35324             draggable: this.draggable,
35325             enableAutocomplete: this.enableAutocomplete,
35326             enableReverseGeocode: this.enableReverseGeocode
35327         });
35328         
35329         var _marker = new google.maps.Marker({
35330             position: position,
35331             map: _map,
35332             title: this.markerTitle,
35333             draggable: this.draggable
35334         });
35335         
35336         return {
35337             map: _map,
35338             marker: _marker,
35339             circle: null,
35340             location: position,
35341             radius: this.radius,
35342             locationName: this.locationName,
35343             addressComponents: {
35344                 formatted_address: null,
35345                 addressLine1: null,
35346                 addressLine2: null,
35347                 streetName: null,
35348                 streetNumber: null,
35349                 city: null,
35350                 district: null,
35351                 state: null,
35352                 stateOrProvince: null
35353             },
35354             settings: this,
35355             domContainer: this.el.dom,
35356             geodecoder: new google.maps.Geocoder()
35357         };
35358     },
35359     
35360     drawCircle: function(center, radius, options) 
35361     {
35362         if (this.gMapContext.circle != null) {
35363             this.gMapContext.circle.setMap(null);
35364         }
35365         if (radius > 0) {
35366             radius *= 1;
35367             options = Roo.apply({}, options, {
35368                 strokeColor: "#0000FF",
35369                 strokeOpacity: .35,
35370                 strokeWeight: 2,
35371                 fillColor: "#0000FF",
35372                 fillOpacity: .2
35373             });
35374             
35375             options.map = this.gMapContext.map;
35376             options.radius = radius;
35377             options.center = center;
35378             this.gMapContext.circle = new google.maps.Circle(options);
35379             return this.gMapContext.circle;
35380         }
35381         
35382         return null;
35383     },
35384     
35385     setPosition: function(location) 
35386     {
35387         this.gMapContext.location = location;
35388         this.gMapContext.marker.setPosition(location);
35389         this.gMapContext.map.panTo(location);
35390         this.drawCircle(location, this.gMapContext.radius, {});
35391         
35392         var _this = this;
35393         
35394         if (this.gMapContext.settings.enableReverseGeocode) {
35395             this.gMapContext.geodecoder.geocode({
35396                 latLng: this.gMapContext.location
35397             }, function(results, status) {
35398                 
35399                 if (status == google.maps.GeocoderStatus.OK && results.length > 0) {
35400                     _this.gMapContext.locationName = results[0].formatted_address;
35401                     _this.gMapContext.addressComponents = _this.address_component_from_google_geocode(results[0].address_components);
35402                     
35403                     _this.fireEvent('positionchanged', this, location);
35404                 }
35405             });
35406             
35407             return;
35408         }
35409         
35410         this.fireEvent('positionchanged', this, location);
35411     },
35412     
35413     resize: function()
35414     {
35415         google.maps.event.trigger(this.gMapContext.map, "resize");
35416         
35417         this.gMapContext.map.setCenter(this.gMapContext.marker.position);
35418         
35419         this.fireEvent('resize', this);
35420     },
35421     
35422     setPositionByLatLng: function(latitude, longitude)
35423     {
35424         this.setPosition(new google.maps.LatLng(latitude, longitude));
35425     },
35426     
35427     getCurrentPosition: function() 
35428     {
35429         return {
35430             latitude: this.gMapContext.location.lat(),
35431             longitude: this.gMapContext.location.lng()
35432         };
35433     },
35434     
35435     getAddressName: function() 
35436     {
35437         return this.gMapContext.locationName;
35438     },
35439     
35440     getAddressComponents: function() 
35441     {
35442         return this.gMapContext.addressComponents;
35443     },
35444     
35445     address_component_from_google_geocode: function(address_components) 
35446     {
35447         var result = {};
35448         
35449         for (var i = 0; i < address_components.length; i++) {
35450             var component = address_components[i];
35451             if (component.types.indexOf("postal_code") >= 0) {
35452                 result.postalCode = component.short_name;
35453             } else if (component.types.indexOf("street_number") >= 0) {
35454                 result.streetNumber = component.short_name;
35455             } else if (component.types.indexOf("route") >= 0) {
35456                 result.streetName = component.short_name;
35457             } else if (component.types.indexOf("neighborhood") >= 0) {
35458                 result.city = component.short_name;
35459             } else if (component.types.indexOf("locality") >= 0) {
35460                 result.city = component.short_name;
35461             } else if (component.types.indexOf("sublocality") >= 0) {
35462                 result.district = component.short_name;
35463             } else if (component.types.indexOf("administrative_area_level_1") >= 0) {
35464                 result.stateOrProvince = component.short_name;
35465             } else if (component.types.indexOf("country") >= 0) {
35466                 result.country = component.short_name;
35467             }
35468         }
35469         
35470         result.addressLine1 = [ result.streetNumber, result.streetName ].join(" ").trim();
35471         result.addressLine2 = "";
35472         return result;
35473     },
35474     
35475     setZoomLevel: function(zoom)
35476     {
35477         this.gMapContext.map.setZoom(zoom);
35478     },
35479     
35480     show: function()
35481     {
35482         if(!this.el){
35483             return;
35484         }
35485         
35486         this.el.show();
35487         
35488         this.resize();
35489         
35490         this.fireEvent('show', this);
35491     },
35492     
35493     hide: function()
35494     {
35495         if(!this.el){
35496             return;
35497         }
35498         
35499         this.el.hide();
35500         
35501         this.fireEvent('hide', this);
35502     }
35503     
35504 });
35505
35506 Roo.apply(Roo.bootstrap.LocationPicker, {
35507     
35508     OverlayView : function(map, options)
35509     {
35510         options = options || {};
35511         
35512         this.setMap(map);
35513     }
35514     
35515     
35516 });/**
35517  * @class Roo.bootstrap.Alert
35518  * @extends Roo.bootstrap.Component
35519  * Bootstrap Alert class - shows an alert area box
35520  * eg
35521  * <div class="alert alert-danger" role="alert"><span class="fa fa-exclamation-triangle"></span><span class="sr-only">Error:</span>
35522   Enter a valid email address
35523 </div>
35524  * @licence LGPL
35525  * @cfg {String} title The title of alert
35526  * @cfg {String} html The content of alert
35527  * @cfg {String} weight (success|info|warning|danger) Weight of the message
35528  * @cfg {String} fa font-awesomeicon
35529  * @cfg {Number} seconds default:-1 Number of seconds until it disapears (-1 means never.)
35530  * @cfg {Boolean} close true to show a x closer
35531  * 
35532  * 
35533  * @constructor
35534  * Create a new alert
35535  * @param {Object} config The config object
35536  */
35537
35538
35539 Roo.bootstrap.Alert = function(config){
35540     Roo.bootstrap.Alert.superclass.constructor.call(this, config);
35541     
35542 };
35543
35544 Roo.extend(Roo.bootstrap.Alert, Roo.bootstrap.Component,  {
35545     
35546     title: '',
35547     html: '',
35548     weight: false,
35549     fa: false,
35550     faicon: false, // BC
35551     close : false,
35552     
35553     
35554     getAutoCreate : function()
35555     {
35556         
35557         var cfg = {
35558             tag : 'div',
35559             cls : 'alert',
35560             cn : [
35561                 {
35562                     tag: 'button',
35563                     type :  "button",
35564                     cls: "close",
35565                     html : '×',
35566                     style : this.close ? '' : 'display:none'
35567                 },
35568                 {
35569                     tag : 'i',
35570                     cls : 'roo-alert-icon'
35571                     
35572                 },
35573                 {
35574                     tag : 'b',
35575                     cls : 'roo-alert-title',
35576                     html : this.title
35577                 },
35578                 {
35579                     tag : 'span',
35580                     cls : 'roo-alert-text',
35581                     html : this.html
35582                 }
35583             ]
35584         };
35585         
35586         if(this.faicon){
35587             cfg.cn[0].cls += ' fa ' + this.faicon;
35588         }
35589         if(this.fa){
35590             cfg.cn[0].cls += ' fa ' + this.fa;
35591         }
35592         
35593         if(this.weight){
35594             cfg.cls += ' alert-' + this.weight;
35595         }
35596         
35597         return cfg;
35598     },
35599     
35600     initEvents: function() 
35601     {
35602         this.el.setVisibilityMode(Roo.Element.DISPLAY);
35603         this.titleEl =  this.el.select('.roo-alert-title',true).first();
35604         this.iconEl = this.el.select('.roo-alert-icon',true).first();
35605         this.htmlEl = this.el.select('.roo-alert-text',true).first();
35606         if (this.seconds > 0) {
35607             this.hide.defer(this.seconds, this);
35608         }
35609     },
35610     /**
35611      * Set the Title Message HTML
35612      * @param {String} html
35613      */
35614     setTitle : function(str)
35615     {
35616         this.titleEl.dom.innerHTML = str;
35617     },
35618      
35619      /**
35620      * Set the Body Message HTML
35621      * @param {String} html
35622      */
35623     setHtml : function(str)
35624     {
35625         this.htmlEl.dom.innerHTML = str;
35626     },
35627     /**
35628      * Set the Weight of the alert
35629      * @param {String} (success|info|warning|danger) weight
35630      */
35631     
35632     setWeight : function(weight)
35633     {
35634         if(this.weight){
35635             this.el.removeClass('alert-' + this.weight);
35636         }
35637         
35638         this.weight = weight;
35639         
35640         this.el.addClass('alert-' + this.weight);
35641     },
35642       /**
35643      * Set the Icon of the alert
35644      * @param {String} see fontawsome names (name without the 'fa-' bit)
35645      */
35646     setIcon : function(icon)
35647     {
35648         if(this.faicon){
35649             this.alertEl.removeClass(['fa', 'fa-' + this.faicon]);
35650         }
35651         
35652         this.faicon = icon;
35653         
35654         this.alertEl.addClass(['fa', 'fa-' + this.faicon]);
35655     },
35656     /**
35657      * Hide the Alert
35658      */
35659     hide: function() 
35660     {
35661         this.el.hide();   
35662     },
35663     /**
35664      * Show the Alert
35665      */
35666     show: function() 
35667     {  
35668         this.el.show();   
35669     }
35670     
35671 });
35672
35673  
35674 /*
35675 * Licence: LGPL
35676 */
35677
35678 /**
35679  * @class Roo.bootstrap.UploadCropbox
35680  * @extends Roo.bootstrap.Component
35681  * Bootstrap UploadCropbox class
35682  * @cfg {String} emptyText show when image has been loaded
35683  * @cfg {String} rotateNotify show when image too small to rotate
35684  * @cfg {Number} errorTimeout default 3000
35685  * @cfg {Number} minWidth default 300
35686  * @cfg {Number} minHeight default 300
35687  * @cfg {Array} buttons default ['rotateLeft', 'pictureBtn', 'rotateRight']
35688  * @cfg {Boolean} isDocument (true|false) default false
35689  * @cfg {String} url action url
35690  * @cfg {String} paramName default 'imageUpload'
35691  * @cfg {String} method default POST
35692  * @cfg {Boolean} loadMask (true|false) default true
35693  * @cfg {Boolean} loadingText default 'Loading...'
35694  * 
35695  * @constructor
35696  * Create a new UploadCropbox
35697  * @param {Object} config The config object
35698  */
35699
35700 Roo.bootstrap.UploadCropbox = function(config){
35701     Roo.bootstrap.UploadCropbox.superclass.constructor.call(this, config);
35702     
35703     this.addEvents({
35704         /**
35705          * @event beforeselectfile
35706          * Fire before select file
35707          * @param {Roo.bootstrap.UploadCropbox} this
35708          */
35709         "beforeselectfile" : true,
35710         /**
35711          * @event initial
35712          * Fire after initEvent
35713          * @param {Roo.bootstrap.UploadCropbox} this
35714          */
35715         "initial" : true,
35716         /**
35717          * @event crop
35718          * Fire after initEvent
35719          * @param {Roo.bootstrap.UploadCropbox} this
35720          * @param {String} data
35721          */
35722         "crop" : true,
35723         /**
35724          * @event prepare
35725          * Fire when preparing the file data
35726          * @param {Roo.bootstrap.UploadCropbox} this
35727          * @param {Object} file
35728          */
35729         "prepare" : true,
35730         /**
35731          * @event exception
35732          * Fire when get exception
35733          * @param {Roo.bootstrap.UploadCropbox} this
35734          * @param {XMLHttpRequest} xhr
35735          */
35736         "exception" : true,
35737         /**
35738          * @event beforeloadcanvas
35739          * Fire before load the canvas
35740          * @param {Roo.bootstrap.UploadCropbox} this
35741          * @param {String} src
35742          */
35743         "beforeloadcanvas" : true,
35744         /**
35745          * @event trash
35746          * Fire when trash image
35747          * @param {Roo.bootstrap.UploadCropbox} this
35748          */
35749         "trash" : true,
35750         /**
35751          * @event download
35752          * Fire when download the image
35753          * @param {Roo.bootstrap.UploadCropbox} this
35754          */
35755         "download" : true,
35756         /**
35757          * @event footerbuttonclick
35758          * Fire when footerbuttonclick
35759          * @param {Roo.bootstrap.UploadCropbox} this
35760          * @param {String} type
35761          */
35762         "footerbuttonclick" : true,
35763         /**
35764          * @event resize
35765          * Fire when resize
35766          * @param {Roo.bootstrap.UploadCropbox} this
35767          */
35768         "resize" : true,
35769         /**
35770          * @event rotate
35771          * Fire when rotate the image
35772          * @param {Roo.bootstrap.UploadCropbox} this
35773          * @param {String} pos
35774          */
35775         "rotate" : true,
35776         /**
35777          * @event inspect
35778          * Fire when inspect the file
35779          * @param {Roo.bootstrap.UploadCropbox} this
35780          * @param {Object} file
35781          */
35782         "inspect" : true,
35783         /**
35784          * @event upload
35785          * Fire when xhr upload the file
35786          * @param {Roo.bootstrap.UploadCropbox} this
35787          * @param {Object} data
35788          */
35789         "upload" : true,
35790         /**
35791          * @event arrange
35792          * Fire when arrange the file data
35793          * @param {Roo.bootstrap.UploadCropbox} this
35794          * @param {Object} formData
35795          */
35796         "arrange" : true
35797     });
35798     
35799     this.buttons = this.buttons || Roo.bootstrap.UploadCropbox.footer.STANDARD;
35800 };
35801
35802 Roo.extend(Roo.bootstrap.UploadCropbox, Roo.bootstrap.Component,  {
35803     
35804     emptyText : 'Click to upload image',
35805     rotateNotify : 'Image is too small to rotate',
35806     errorTimeout : 3000,
35807     scale : 0,
35808     baseScale : 1,
35809     rotate : 0,
35810     dragable : false,
35811     pinching : false,
35812     mouseX : 0,
35813     mouseY : 0,
35814     cropData : false,
35815     minWidth : 300,
35816     minHeight : 300,
35817     file : false,
35818     exif : {},
35819     baseRotate : 1,
35820     cropType : 'image/jpeg',
35821     buttons : false,
35822     canvasLoaded : false,
35823     isDocument : false,
35824     method : 'POST',
35825     paramName : 'imageUpload',
35826     loadMask : true,
35827     loadingText : 'Loading...',
35828     maskEl : false,
35829     
35830     getAutoCreate : function()
35831     {
35832         var cfg = {
35833             tag : 'div',
35834             cls : 'roo-upload-cropbox',
35835             cn : [
35836                 {
35837                     tag : 'input',
35838                     cls : 'roo-upload-cropbox-selector',
35839                     type : 'file'
35840                 },
35841                 {
35842                     tag : 'div',
35843                     cls : 'roo-upload-cropbox-body',
35844                     style : 'cursor:pointer',
35845                     cn : [
35846                         {
35847                             tag : 'div',
35848                             cls : 'roo-upload-cropbox-preview'
35849                         },
35850                         {
35851                             tag : 'div',
35852                             cls : 'roo-upload-cropbox-thumb'
35853                         },
35854                         {
35855                             tag : 'div',
35856                             cls : 'roo-upload-cropbox-empty-notify',
35857                             html : this.emptyText
35858                         },
35859                         {
35860                             tag : 'div',
35861                             cls : 'roo-upload-cropbox-error-notify alert alert-danger',
35862                             html : this.rotateNotify
35863                         }
35864                     ]
35865                 },
35866                 {
35867                     tag : 'div',
35868                     cls : 'roo-upload-cropbox-footer',
35869                     cn : {
35870                         tag : 'div',
35871                         cls : 'btn-group btn-group-justified roo-upload-cropbox-btn-group',
35872                         cn : []
35873                     }
35874                 }
35875             ]
35876         };
35877         
35878         return cfg;
35879     },
35880     
35881     onRender : function(ct, position)
35882     {
35883         Roo.bootstrap.UploadCropbox.superclass.onRender.call(this, ct, position);
35884         
35885         if (this.buttons.length) {
35886             
35887             Roo.each(this.buttons, function(bb) {
35888                 
35889                 var btn = this.el.select('.roo-upload-cropbox-footer div.roo-upload-cropbox-btn-group').first().createChild(bb);
35890                 
35891                 btn.on('click', this.onFooterButtonClick.createDelegate(this, [bb.action], true));
35892                 
35893             }, this);
35894         }
35895         
35896         if(this.loadMask){
35897             this.maskEl = this.el;
35898         }
35899     },
35900     
35901     initEvents : function()
35902     {
35903         this.urlAPI = (window.createObjectURL && window) || 
35904                                 (window.URL && URL.revokeObjectURL && URL) || 
35905                                 (window.webkitURL && webkitURL);
35906                         
35907         this.bodyEl = this.el.select('.roo-upload-cropbox-body', true).first();
35908         this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
35909         
35910         this.selectorEl = this.el.select('.roo-upload-cropbox-selector', true).first();
35911         this.selectorEl.hide();
35912         
35913         this.previewEl = this.el.select('.roo-upload-cropbox-preview', true).first();
35914         this.previewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
35915         
35916         this.thumbEl = this.el.select('.roo-upload-cropbox-thumb', true).first();
35917         this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
35918         this.thumbEl.hide();
35919         
35920         this.notifyEl = this.el.select('.roo-upload-cropbox-empty-notify', true).first();
35921         this.notifyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
35922         
35923         this.errorEl = this.el.select('.roo-upload-cropbox-error-notify', true).first();
35924         this.errorEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
35925         this.errorEl.hide();
35926         
35927         this.footerEl = this.el.select('.roo-upload-cropbox-footer', true).first();
35928         this.footerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
35929         this.footerEl.hide();
35930         
35931         this.setThumbBoxSize();
35932         
35933         this.bind();
35934         
35935         this.resize();
35936         
35937         this.fireEvent('initial', this);
35938     },
35939
35940     bind : function()
35941     {
35942         var _this = this;
35943         
35944         window.addEventListener("resize", function() { _this.resize(); } );
35945         
35946         this.bodyEl.on('click', this.beforeSelectFile, this);
35947         
35948         if(Roo.isTouch){
35949             this.bodyEl.on('touchstart', this.onTouchStart, this);
35950             this.bodyEl.on('touchmove', this.onTouchMove, this);
35951             this.bodyEl.on('touchend', this.onTouchEnd, this);
35952         }
35953         
35954         if(!Roo.isTouch){
35955             this.bodyEl.on('mousedown', this.onMouseDown, this);
35956             this.bodyEl.on('mousemove', this.onMouseMove, this);
35957             var mousewheel = (/Firefox/i.test(navigator.userAgent))? 'DOMMouseScroll' : 'mousewheel';
35958             this.bodyEl.on(mousewheel, this.onMouseWheel, this);
35959             Roo.get(document).on('mouseup', this.onMouseUp, this);
35960         }
35961         
35962         this.selectorEl.on('change', this.onFileSelected, this);
35963     },
35964     
35965     reset : function()
35966     {    
35967         this.scale = 0;
35968         this.baseScale = 1;
35969         this.rotate = 0;
35970         this.baseRotate = 1;
35971         this.dragable = false;
35972         this.pinching = false;
35973         this.mouseX = 0;
35974         this.mouseY = 0;
35975         this.cropData = false;
35976         this.notifyEl.dom.innerHTML = this.emptyText;
35977         
35978         this.selectorEl.dom.value = '';
35979         
35980     },
35981     
35982     resize : function()
35983     {
35984         if(this.fireEvent('resize', this) != false){
35985             this.setThumbBoxPosition();
35986             this.setCanvasPosition();
35987         }
35988     },
35989     
35990     onFooterButtonClick : function(e, el, o, type)
35991     {
35992         switch (type) {
35993             case 'rotate-left' :
35994                 this.onRotateLeft(e);
35995                 break;
35996             case 'rotate-right' :
35997                 this.onRotateRight(e);
35998                 break;
35999             case 'picture' :
36000                 this.beforeSelectFile(e);
36001                 break;
36002             case 'trash' :
36003                 this.trash(e);
36004                 break;
36005             case 'crop' :
36006                 this.crop(e);
36007                 break;
36008             case 'download' :
36009                 this.download(e);
36010                 break;
36011             default :
36012                 break;
36013         }
36014         
36015         this.fireEvent('footerbuttonclick', this, type);
36016     },
36017     
36018     beforeSelectFile : function(e)
36019     {
36020         e.preventDefault();
36021         
36022         if(this.fireEvent('beforeselectfile', this) != false){
36023             this.selectorEl.dom.click();
36024         }
36025     },
36026     
36027     onFileSelected : function(e)
36028     {
36029         e.preventDefault();
36030         
36031         if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
36032             return;
36033         }
36034         
36035         var file = this.selectorEl.dom.files[0];
36036         
36037         if(this.fireEvent('inspect', this, file) != false){
36038             this.prepare(file);
36039         }
36040         
36041     },
36042     
36043     trash : function(e)
36044     {
36045         this.fireEvent('trash', this);
36046     },
36047     
36048     download : function(e)
36049     {
36050         this.fireEvent('download', this);
36051     },
36052     
36053     loadCanvas : function(src)
36054     {   
36055         if(this.fireEvent('beforeloadcanvas', this, src) != false){
36056             
36057             this.reset();
36058             
36059             this.imageEl = document.createElement('img');
36060             
36061             var _this = this;
36062             
36063             this.imageEl.addEventListener("load", function(){ _this.onLoadCanvas(); });
36064             
36065             this.imageEl.src = src;
36066         }
36067     },
36068     
36069     onLoadCanvas : function()
36070     {   
36071         this.imageEl.OriginWidth = this.imageEl.naturalWidth || this.imageEl.width;
36072         this.imageEl.OriginHeight = this.imageEl.naturalHeight || this.imageEl.height;
36073         
36074         this.bodyEl.un('click', this.beforeSelectFile, this);
36075         
36076         this.notifyEl.hide();
36077         this.thumbEl.show();
36078         this.footerEl.show();
36079         
36080         this.baseRotateLevel();
36081         
36082         if(this.isDocument){
36083             this.setThumbBoxSize();
36084         }
36085         
36086         this.setThumbBoxPosition();
36087         
36088         this.baseScaleLevel();
36089         
36090         this.draw();
36091         
36092         this.resize();
36093         
36094         this.canvasLoaded = true;
36095         
36096         if(this.loadMask){
36097             this.maskEl.unmask();
36098         }
36099         
36100     },
36101     
36102     setCanvasPosition : function()
36103     {   
36104         if(!this.canvasEl){
36105             return;
36106         }
36107         
36108         var pw = Math.ceil((this.bodyEl.getWidth() - this.canvasEl.width) / 2);
36109         var ph = Math.ceil((this.bodyEl.getHeight() - this.canvasEl.height) / 2);
36110         
36111         this.previewEl.setLeft(pw);
36112         this.previewEl.setTop(ph);
36113         
36114     },
36115     
36116     onMouseDown : function(e)
36117     {   
36118         e.stopEvent();
36119         
36120         this.dragable = true;
36121         this.pinching = false;
36122         
36123         if(this.isDocument && (this.canvasEl.width < this.thumbEl.getWidth() || this.canvasEl.height < this.thumbEl.getHeight())){
36124             this.dragable = false;
36125             return;
36126         }
36127         
36128         this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
36129         this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
36130         
36131     },
36132     
36133     onMouseMove : function(e)
36134     {   
36135         e.stopEvent();
36136         
36137         if(!this.canvasLoaded){
36138             return;
36139         }
36140         
36141         if (!this.dragable){
36142             return;
36143         }
36144         
36145         var minX = Math.ceil(this.thumbEl.getLeft(true));
36146         var minY = Math.ceil(this.thumbEl.getTop(true));
36147         
36148         var maxX = Math.ceil(minX + this.thumbEl.getWidth() - this.canvasEl.width);
36149         var maxY = Math.ceil(minY + this.thumbEl.getHeight() - this.canvasEl.height);
36150         
36151         var x = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
36152         var y = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
36153         
36154         x = x - this.mouseX;
36155         y = y - this.mouseY;
36156         
36157         var bgX = Math.ceil(x + this.previewEl.getLeft(true));
36158         var bgY = Math.ceil(y + this.previewEl.getTop(true));
36159         
36160         bgX = (minX < bgX) ? minX : ((maxX > bgX) ? maxX : bgX);
36161         bgY = (minY < bgY) ? minY : ((maxY > bgY) ? maxY : bgY);
36162         
36163         this.previewEl.setLeft(bgX);
36164         this.previewEl.setTop(bgY);
36165         
36166         this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
36167         this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
36168     },
36169     
36170     onMouseUp : function(e)
36171     {   
36172         e.stopEvent();
36173         
36174         this.dragable = false;
36175     },
36176     
36177     onMouseWheel : function(e)
36178     {   
36179         e.stopEvent();
36180         
36181         this.startScale = this.scale;
36182         
36183         this.scale = (e.getWheelDelta() == 1) ? (this.scale + 1) : (this.scale - 1);
36184         
36185         if(!this.zoomable()){
36186             this.scale = this.startScale;
36187             return;
36188         }
36189         
36190         this.draw();
36191         
36192         return;
36193     },
36194     
36195     zoomable : function()
36196     {
36197         var minScale = this.thumbEl.getWidth() / this.minWidth;
36198         
36199         if(this.minWidth < this.minHeight){
36200             minScale = this.thumbEl.getHeight() / this.minHeight;
36201         }
36202         
36203         var width = Math.ceil(this.imageEl.OriginWidth * this.getScaleLevel() / minScale);
36204         var height = Math.ceil(this.imageEl.OriginHeight * this.getScaleLevel() / minScale);
36205         
36206         if(
36207                 this.isDocument &&
36208                 (this.rotate == 0 || this.rotate == 180) && 
36209                 (
36210                     width > this.imageEl.OriginWidth || 
36211                     height > this.imageEl.OriginHeight ||
36212                     (width < this.minWidth && height < this.minHeight)
36213                 )
36214         ){
36215             return false;
36216         }
36217         
36218         if(
36219                 this.isDocument &&
36220                 (this.rotate == 90 || this.rotate == 270) && 
36221                 (
36222                     width > this.imageEl.OriginWidth || 
36223                     height > this.imageEl.OriginHeight ||
36224                     (width < this.minHeight && height < this.minWidth)
36225                 )
36226         ){
36227             return false;
36228         }
36229         
36230         if(
36231                 !this.isDocument &&
36232                 (this.rotate == 0 || this.rotate == 180) && 
36233                 (
36234                     width < this.minWidth || 
36235                     width > this.imageEl.OriginWidth || 
36236                     height < this.minHeight || 
36237                     height > this.imageEl.OriginHeight
36238                 )
36239         ){
36240             return false;
36241         }
36242         
36243         if(
36244                 !this.isDocument &&
36245                 (this.rotate == 90 || this.rotate == 270) && 
36246                 (
36247                     width < this.minHeight || 
36248                     width > this.imageEl.OriginWidth || 
36249                     height < this.minWidth || 
36250                     height > this.imageEl.OriginHeight
36251                 )
36252         ){
36253             return false;
36254         }
36255         
36256         return true;
36257         
36258     },
36259     
36260     onRotateLeft : function(e)
36261     {   
36262         if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
36263             
36264             var minScale = this.thumbEl.getWidth() / this.minWidth;
36265             
36266             var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
36267             var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
36268             
36269             this.startScale = this.scale;
36270             
36271             while (this.getScaleLevel() < minScale){
36272             
36273                 this.scale = this.scale + 1;
36274                 
36275                 if(!this.zoomable()){
36276                     break;
36277                 }
36278                 
36279                 if(
36280                         Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
36281                         Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
36282                 ){
36283                     continue;
36284                 }
36285                 
36286                 this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
36287
36288                 this.draw();
36289                 
36290                 return;
36291             }
36292             
36293             this.scale = this.startScale;
36294             
36295             this.onRotateFail();
36296             
36297             return false;
36298         }
36299         
36300         this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
36301
36302         if(this.isDocument){
36303             this.setThumbBoxSize();
36304             this.setThumbBoxPosition();
36305             this.setCanvasPosition();
36306         }
36307         
36308         this.draw();
36309         
36310         this.fireEvent('rotate', this, 'left');
36311         
36312     },
36313     
36314     onRotateRight : function(e)
36315     {
36316         if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
36317             
36318             var minScale = this.thumbEl.getWidth() / this.minWidth;
36319         
36320             var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
36321             var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
36322             
36323             this.startScale = this.scale;
36324             
36325             while (this.getScaleLevel() < minScale){
36326             
36327                 this.scale = this.scale + 1;
36328                 
36329                 if(!this.zoomable()){
36330                     break;
36331                 }
36332                 
36333                 if(
36334                         Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
36335                         Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
36336                 ){
36337                     continue;
36338                 }
36339                 
36340                 this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
36341
36342                 this.draw();
36343                 
36344                 return;
36345             }
36346             
36347             this.scale = this.startScale;
36348             
36349             this.onRotateFail();
36350             
36351             return false;
36352         }
36353         
36354         this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
36355
36356         if(this.isDocument){
36357             this.setThumbBoxSize();
36358             this.setThumbBoxPosition();
36359             this.setCanvasPosition();
36360         }
36361         
36362         this.draw();
36363         
36364         this.fireEvent('rotate', this, 'right');
36365     },
36366     
36367     onRotateFail : function()
36368     {
36369         this.errorEl.show(true);
36370         
36371         var _this = this;
36372         
36373         (function() { _this.errorEl.hide(true); }).defer(this.errorTimeout);
36374     },
36375     
36376     draw : function()
36377     {
36378         this.previewEl.dom.innerHTML = '';
36379         
36380         var canvasEl = document.createElement("canvas");
36381         
36382         var contextEl = canvasEl.getContext("2d");
36383         
36384         canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
36385         canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
36386         var center = this.imageEl.OriginWidth / 2;
36387         
36388         if(this.imageEl.OriginWidth < this.imageEl.OriginHeight){
36389             canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
36390             canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
36391             center = this.imageEl.OriginHeight / 2;
36392         }
36393         
36394         contextEl.scale(this.getScaleLevel(), this.getScaleLevel());
36395         
36396         contextEl.translate(center, center);
36397         contextEl.rotate(this.rotate * Math.PI / 180);
36398
36399         contextEl.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
36400         
36401         this.canvasEl = document.createElement("canvas");
36402         
36403         this.contextEl = this.canvasEl.getContext("2d");
36404         
36405         switch (this.rotate) {
36406             case 0 :
36407                 
36408                 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
36409                 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
36410                 
36411                 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
36412                 
36413                 break;
36414             case 90 : 
36415                 
36416                 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
36417                 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
36418                 
36419                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
36420                     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);
36421                     break;
36422                 }
36423                 
36424                 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
36425                 
36426                 break;
36427             case 180 :
36428                 
36429                 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
36430                 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
36431                 
36432                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
36433                     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);
36434                     break;
36435                 }
36436                 
36437                 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);
36438                 
36439                 break;
36440             case 270 :
36441                 
36442                 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
36443                 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
36444         
36445                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
36446                     this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
36447                     break;
36448                 }
36449                 
36450                 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);
36451                 
36452                 break;
36453             default : 
36454                 break;
36455         }
36456         
36457         this.previewEl.appendChild(this.canvasEl);
36458         
36459         this.setCanvasPosition();
36460     },
36461     
36462     crop : function()
36463     {
36464         if(!this.canvasLoaded){
36465             return;
36466         }
36467         
36468         var imageCanvas = document.createElement("canvas");
36469         
36470         var imageContext = imageCanvas.getContext("2d");
36471         
36472         imageCanvas.width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
36473         imageCanvas.height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
36474         
36475         var center = imageCanvas.width / 2;
36476         
36477         imageContext.translate(center, center);
36478         
36479         imageContext.rotate(this.rotate * Math.PI / 180);
36480         
36481         imageContext.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
36482         
36483         var canvas = document.createElement("canvas");
36484         
36485         var context = canvas.getContext("2d");
36486                 
36487         canvas.width = this.minWidth;
36488         canvas.height = this.minHeight;
36489
36490         switch (this.rotate) {
36491             case 0 :
36492                 
36493                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
36494                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
36495                 
36496                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
36497                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
36498                 
36499                 var targetWidth = this.minWidth - 2 * x;
36500                 var targetHeight = this.minHeight - 2 * y;
36501                 
36502                 var scale = 1;
36503                 
36504                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
36505                     scale = targetWidth / width;
36506                 }
36507                 
36508                 if(x > 0 && y == 0){
36509                     scale = targetHeight / height;
36510                 }
36511                 
36512                 if(x > 0 && y > 0){
36513                     scale = targetWidth / width;
36514                     
36515                     if(width < height){
36516                         scale = targetHeight / height;
36517                     }
36518                 }
36519                 
36520                 context.scale(scale, scale);
36521                 
36522                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
36523                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
36524
36525                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
36526                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
36527
36528                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
36529                 
36530                 break;
36531             case 90 : 
36532                 
36533                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
36534                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
36535                 
36536                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
36537                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
36538                 
36539                 var targetWidth = this.minWidth - 2 * x;
36540                 var targetHeight = this.minHeight - 2 * y;
36541                 
36542                 var scale = 1;
36543                 
36544                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
36545                     scale = targetWidth / width;
36546                 }
36547                 
36548                 if(x > 0 && y == 0){
36549                     scale = targetHeight / height;
36550                 }
36551                 
36552                 if(x > 0 && y > 0){
36553                     scale = targetWidth / width;
36554                     
36555                     if(width < height){
36556                         scale = targetHeight / height;
36557                     }
36558                 }
36559                 
36560                 context.scale(scale, scale);
36561                 
36562                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
36563                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
36564
36565                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
36566                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
36567                 
36568                 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
36569                 
36570                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
36571                 
36572                 break;
36573             case 180 :
36574                 
36575                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
36576                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
36577                 
36578                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
36579                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
36580                 
36581                 var targetWidth = this.minWidth - 2 * x;
36582                 var targetHeight = this.minHeight - 2 * y;
36583                 
36584                 var scale = 1;
36585                 
36586                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
36587                     scale = targetWidth / width;
36588                 }
36589                 
36590                 if(x > 0 && y == 0){
36591                     scale = targetHeight / height;
36592                 }
36593                 
36594                 if(x > 0 && y > 0){
36595                     scale = targetWidth / width;
36596                     
36597                     if(width < height){
36598                         scale = targetHeight / height;
36599                     }
36600                 }
36601                 
36602                 context.scale(scale, scale);
36603                 
36604                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
36605                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
36606
36607                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
36608                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
36609
36610                 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
36611                 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
36612                 
36613                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
36614                 
36615                 break;
36616             case 270 :
36617                 
36618                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
36619                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
36620                 
36621                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
36622                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
36623                 
36624                 var targetWidth = this.minWidth - 2 * x;
36625                 var targetHeight = this.minHeight - 2 * y;
36626                 
36627                 var scale = 1;
36628                 
36629                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
36630                     scale = targetWidth / width;
36631                 }
36632                 
36633                 if(x > 0 && y == 0){
36634                     scale = targetHeight / height;
36635                 }
36636                 
36637                 if(x > 0 && y > 0){
36638                     scale = targetWidth / width;
36639                     
36640                     if(width < height){
36641                         scale = targetHeight / height;
36642                     }
36643                 }
36644                 
36645                 context.scale(scale, scale);
36646                 
36647                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
36648                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
36649
36650                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
36651                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
36652                 
36653                 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
36654                 
36655                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
36656                 
36657                 break;
36658             default : 
36659                 break;
36660         }
36661         
36662         this.cropData = canvas.toDataURL(this.cropType);
36663         
36664         if(this.fireEvent('crop', this, this.cropData) !== false){
36665             this.process(this.file, this.cropData);
36666         }
36667         
36668         return;
36669         
36670     },
36671     
36672     setThumbBoxSize : function()
36673     {
36674         var width, height;
36675         
36676         if(this.isDocument && typeof(this.imageEl) != 'undefined'){
36677             width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.max(this.minWidth, this.minHeight) : Math.min(this.minWidth, this.minHeight);
36678             height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.min(this.minWidth, this.minHeight) : Math.max(this.minWidth, this.minHeight);
36679             
36680             this.minWidth = width;
36681             this.minHeight = height;
36682             
36683             if(this.rotate == 90 || this.rotate == 270){
36684                 this.minWidth = height;
36685                 this.minHeight = width;
36686             }
36687         }
36688         
36689         height = 300;
36690         width = Math.ceil(this.minWidth * height / this.minHeight);
36691         
36692         if(this.minWidth > this.minHeight){
36693             width = 300;
36694             height = Math.ceil(this.minHeight * width / this.minWidth);
36695         }
36696         
36697         this.thumbEl.setStyle({
36698             width : width + 'px',
36699             height : height + 'px'
36700         });
36701
36702         return;
36703             
36704     },
36705     
36706     setThumbBoxPosition : function()
36707     {
36708         var x = Math.ceil((this.bodyEl.getWidth() - this.thumbEl.getWidth()) / 2 );
36709         var y = Math.ceil((this.bodyEl.getHeight() - this.thumbEl.getHeight()) / 2);
36710         
36711         this.thumbEl.setLeft(x);
36712         this.thumbEl.setTop(y);
36713         
36714     },
36715     
36716     baseRotateLevel : function()
36717     {
36718         this.baseRotate = 1;
36719         
36720         if(
36721                 typeof(this.exif) != 'undefined' &&
36722                 typeof(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != 'undefined' &&
36723                 [1, 3, 6, 8].indexOf(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != -1
36724         ){
36725             this.baseRotate = this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']];
36726         }
36727         
36728         this.rotate = Roo.bootstrap.UploadCropbox['Orientation'][this.baseRotate];
36729         
36730     },
36731     
36732     baseScaleLevel : function()
36733     {
36734         var width, height;
36735         
36736         if(this.isDocument){
36737             
36738             if(this.baseRotate == 6 || this.baseRotate == 8){
36739             
36740                 height = this.thumbEl.getHeight();
36741                 this.baseScale = height / this.imageEl.OriginWidth;
36742
36743                 if(this.imageEl.OriginHeight * this.baseScale > this.thumbEl.getWidth()){
36744                     width = this.thumbEl.getWidth();
36745                     this.baseScale = width / this.imageEl.OriginHeight;
36746                 }
36747
36748                 return;
36749             }
36750
36751             height = this.thumbEl.getHeight();
36752             this.baseScale = height / this.imageEl.OriginHeight;
36753
36754             if(this.imageEl.OriginWidth * this.baseScale > this.thumbEl.getWidth()){
36755                 width = this.thumbEl.getWidth();
36756                 this.baseScale = width / this.imageEl.OriginWidth;
36757             }
36758
36759             return;
36760         }
36761         
36762         if(this.baseRotate == 6 || this.baseRotate == 8){
36763             
36764             width = this.thumbEl.getHeight();
36765             this.baseScale = width / this.imageEl.OriginHeight;
36766             
36767             if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getWidth()){
36768                 height = this.thumbEl.getWidth();
36769                 this.baseScale = height / this.imageEl.OriginHeight;
36770             }
36771             
36772             if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
36773                 height = this.thumbEl.getWidth();
36774                 this.baseScale = height / this.imageEl.OriginHeight;
36775                 
36776                 if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getHeight()){
36777                     width = this.thumbEl.getHeight();
36778                     this.baseScale = width / this.imageEl.OriginWidth;
36779                 }
36780             }
36781             
36782             return;
36783         }
36784         
36785         width = this.thumbEl.getWidth();
36786         this.baseScale = width / this.imageEl.OriginWidth;
36787         
36788         if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getHeight()){
36789             height = this.thumbEl.getHeight();
36790             this.baseScale = height / this.imageEl.OriginHeight;
36791         }
36792         
36793         if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
36794             
36795             height = this.thumbEl.getHeight();
36796             this.baseScale = height / this.imageEl.OriginHeight;
36797             
36798             if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getWidth()){
36799                 width = this.thumbEl.getWidth();
36800                 this.baseScale = width / this.imageEl.OriginWidth;
36801             }
36802             
36803         }
36804         
36805         return;
36806     },
36807     
36808     getScaleLevel : function()
36809     {
36810         return this.baseScale * Math.pow(1.1, this.scale);
36811     },
36812     
36813     onTouchStart : function(e)
36814     {
36815         if(!this.canvasLoaded){
36816             this.beforeSelectFile(e);
36817             return;
36818         }
36819         
36820         var touches = e.browserEvent.touches;
36821         
36822         if(!touches){
36823             return;
36824         }
36825         
36826         if(touches.length == 1){
36827             this.onMouseDown(e);
36828             return;
36829         }
36830         
36831         if(touches.length != 2){
36832             return;
36833         }
36834         
36835         var coords = [];
36836         
36837         for(var i = 0, finger; finger = touches[i]; i++){
36838             coords.push(finger.pageX, finger.pageY);
36839         }
36840         
36841         var x = Math.pow(coords[0] - coords[2], 2);
36842         var y = Math.pow(coords[1] - coords[3], 2);
36843         
36844         this.startDistance = Math.sqrt(x + y);
36845         
36846         this.startScale = this.scale;
36847         
36848         this.pinching = true;
36849         this.dragable = false;
36850         
36851     },
36852     
36853     onTouchMove : function(e)
36854     {
36855         if(!this.pinching && !this.dragable){
36856             return;
36857         }
36858         
36859         var touches = e.browserEvent.touches;
36860         
36861         if(!touches){
36862             return;
36863         }
36864         
36865         if(this.dragable){
36866             this.onMouseMove(e);
36867             return;
36868         }
36869         
36870         var coords = [];
36871         
36872         for(var i = 0, finger; finger = touches[i]; i++){
36873             coords.push(finger.pageX, finger.pageY);
36874         }
36875         
36876         var x = Math.pow(coords[0] - coords[2], 2);
36877         var y = Math.pow(coords[1] - coords[3], 2);
36878         
36879         this.endDistance = Math.sqrt(x + y);
36880         
36881         this.scale = this.startScale + Math.floor(Math.log(this.endDistance / this.startDistance) / Math.log(1.1));
36882         
36883         if(!this.zoomable()){
36884             this.scale = this.startScale;
36885             return;
36886         }
36887         
36888         this.draw();
36889         
36890     },
36891     
36892     onTouchEnd : function(e)
36893     {
36894         this.pinching = false;
36895         this.dragable = false;
36896         
36897     },
36898     
36899     process : function(file, crop)
36900     {
36901         if(this.loadMask){
36902             this.maskEl.mask(this.loadingText);
36903         }
36904         
36905         this.xhr = new XMLHttpRequest();
36906         
36907         file.xhr = this.xhr;
36908
36909         this.xhr.open(this.method, this.url, true);
36910         
36911         var headers = {
36912             "Accept": "application/json",
36913             "Cache-Control": "no-cache",
36914             "X-Requested-With": "XMLHttpRequest"
36915         };
36916         
36917         for (var headerName in headers) {
36918             var headerValue = headers[headerName];
36919             if (headerValue) {
36920                 this.xhr.setRequestHeader(headerName, headerValue);
36921             }
36922         }
36923         
36924         var _this = this;
36925         
36926         this.xhr.onload = function()
36927         {
36928             _this.xhrOnLoad(_this.xhr);
36929         }
36930         
36931         this.xhr.onerror = function()
36932         {
36933             _this.xhrOnError(_this.xhr);
36934         }
36935         
36936         var formData = new FormData();
36937
36938         formData.append('returnHTML', 'NO');
36939         
36940         if(crop){
36941             formData.append('crop', crop);
36942         }
36943         
36944         if(typeof(file) != 'undefined' && (typeof(file.id) == 'undefined' || file.id * 1 < 1)){
36945             formData.append(this.paramName, file, file.name);
36946         }
36947         
36948         if(typeof(file.filename) != 'undefined'){
36949             formData.append('filename', file.filename);
36950         }
36951         
36952         if(typeof(file.mimetype) != 'undefined'){
36953             formData.append('mimetype', file.mimetype);
36954         }
36955         
36956         if(this.fireEvent('arrange', this, formData) != false){
36957             this.xhr.send(formData);
36958         };
36959     },
36960     
36961     xhrOnLoad : function(xhr)
36962     {
36963         if(this.loadMask){
36964             this.maskEl.unmask();
36965         }
36966         
36967         if (xhr.readyState !== 4) {
36968             this.fireEvent('exception', this, xhr);
36969             return;
36970         }
36971
36972         var response = Roo.decode(xhr.responseText);
36973         
36974         if(!response.success){
36975             this.fireEvent('exception', this, xhr);
36976             return;
36977         }
36978         
36979         var response = Roo.decode(xhr.responseText);
36980         
36981         this.fireEvent('upload', this, response);
36982         
36983     },
36984     
36985     xhrOnError : function()
36986     {
36987         if(this.loadMask){
36988             this.maskEl.unmask();
36989         }
36990         
36991         Roo.log('xhr on error');
36992         
36993         var response = Roo.decode(xhr.responseText);
36994           
36995         Roo.log(response);
36996         
36997     },
36998     
36999     prepare : function(file)
37000     {   
37001         if(this.loadMask){
37002             this.maskEl.mask(this.loadingText);
37003         }
37004         
37005         this.file = false;
37006         this.exif = {};
37007         
37008         if(typeof(file) === 'string'){
37009             this.loadCanvas(file);
37010             return;
37011         }
37012         
37013         if(!file || !this.urlAPI){
37014             return;
37015         }
37016         
37017         this.file = file;
37018         this.cropType = file.type;
37019         
37020         var _this = this;
37021         
37022         if(this.fireEvent('prepare', this, this.file) != false){
37023             
37024             var reader = new FileReader();
37025             
37026             reader.onload = function (e) {
37027                 if (e.target.error) {
37028                     Roo.log(e.target.error);
37029                     return;
37030                 }
37031                 
37032                 var buffer = e.target.result,
37033                     dataView = new DataView(buffer),
37034                     offset = 2,
37035                     maxOffset = dataView.byteLength - 4,
37036                     markerBytes,
37037                     markerLength;
37038                 
37039                 if (dataView.getUint16(0) === 0xffd8) {
37040                     while (offset < maxOffset) {
37041                         markerBytes = dataView.getUint16(offset);
37042                         
37043                         if ((markerBytes >= 0xffe0 && markerBytes <= 0xffef) || markerBytes === 0xfffe) {
37044                             markerLength = dataView.getUint16(offset + 2) + 2;
37045                             if (offset + markerLength > dataView.byteLength) {
37046                                 Roo.log('Invalid meta data: Invalid segment size.');
37047                                 break;
37048                             }
37049                             
37050                             if(markerBytes == 0xffe1){
37051                                 _this.parseExifData(
37052                                     dataView,
37053                                     offset,
37054                                     markerLength
37055                                 );
37056                             }
37057                             
37058                             offset += markerLength;
37059                             
37060                             continue;
37061                         }
37062                         
37063                         break;
37064                     }
37065                     
37066                 }
37067                 
37068                 var url = _this.urlAPI.createObjectURL(_this.file);
37069                 
37070                 _this.loadCanvas(url);
37071                 
37072                 return;
37073             }
37074             
37075             reader.readAsArrayBuffer(this.file);
37076             
37077         }
37078         
37079     },
37080     
37081     parseExifData : function(dataView, offset, length)
37082     {
37083         var tiffOffset = offset + 10,
37084             littleEndian,
37085             dirOffset;
37086     
37087         if (dataView.getUint32(offset + 4) !== 0x45786966) {
37088             // No Exif data, might be XMP data instead
37089             return;
37090         }
37091         
37092         // Check for the ASCII code for "Exif" (0x45786966):
37093         if (dataView.getUint32(offset + 4) !== 0x45786966) {
37094             // No Exif data, might be XMP data instead
37095             return;
37096         }
37097         if (tiffOffset + 8 > dataView.byteLength) {
37098             Roo.log('Invalid Exif data: Invalid segment size.');
37099             return;
37100         }
37101         // Check for the two null bytes:
37102         if (dataView.getUint16(offset + 8) !== 0x0000) {
37103             Roo.log('Invalid Exif data: Missing byte alignment offset.');
37104             return;
37105         }
37106         // Check the byte alignment:
37107         switch (dataView.getUint16(tiffOffset)) {
37108         case 0x4949:
37109             littleEndian = true;
37110             break;
37111         case 0x4D4D:
37112             littleEndian = false;
37113             break;
37114         default:
37115             Roo.log('Invalid Exif data: Invalid byte alignment marker.');
37116             return;
37117         }
37118         // Check for the TIFF tag marker (0x002A):
37119         if (dataView.getUint16(tiffOffset + 2, littleEndian) !== 0x002A) {
37120             Roo.log('Invalid Exif data: Missing TIFF marker.');
37121             return;
37122         }
37123         // Retrieve the directory offset bytes, usually 0x00000008 or 8 decimal:
37124         dirOffset = dataView.getUint32(tiffOffset + 4, littleEndian);
37125         
37126         this.parseExifTags(
37127             dataView,
37128             tiffOffset,
37129             tiffOffset + dirOffset,
37130             littleEndian
37131         );
37132     },
37133     
37134     parseExifTags : function(dataView, tiffOffset, dirOffset, littleEndian)
37135     {
37136         var tagsNumber,
37137             dirEndOffset,
37138             i;
37139         if (dirOffset + 6 > dataView.byteLength) {
37140             Roo.log('Invalid Exif data: Invalid directory offset.');
37141             return;
37142         }
37143         tagsNumber = dataView.getUint16(dirOffset, littleEndian);
37144         dirEndOffset = dirOffset + 2 + 12 * tagsNumber;
37145         if (dirEndOffset + 4 > dataView.byteLength) {
37146             Roo.log('Invalid Exif data: Invalid directory size.');
37147             return;
37148         }
37149         for (i = 0; i < tagsNumber; i += 1) {
37150             this.parseExifTag(
37151                 dataView,
37152                 tiffOffset,
37153                 dirOffset + 2 + 12 * i, // tag offset
37154                 littleEndian
37155             );
37156         }
37157         // Return the offset to the next directory:
37158         return dataView.getUint32(dirEndOffset, littleEndian);
37159     },
37160     
37161     parseExifTag : function (dataView, tiffOffset, offset, littleEndian) 
37162     {
37163         var tag = dataView.getUint16(offset, littleEndian);
37164         
37165         this.exif[tag] = this.getExifValue(
37166             dataView,
37167             tiffOffset,
37168             offset,
37169             dataView.getUint16(offset + 2, littleEndian), // tag type
37170             dataView.getUint32(offset + 4, littleEndian), // tag length
37171             littleEndian
37172         );
37173     },
37174     
37175     getExifValue : function (dataView, tiffOffset, offset, type, length, littleEndian)
37176     {
37177         var tagType = Roo.bootstrap.UploadCropbox.exifTagTypes[type],
37178             tagSize,
37179             dataOffset,
37180             values,
37181             i,
37182             str,
37183             c;
37184     
37185         if (!tagType) {
37186             Roo.log('Invalid Exif data: Invalid tag type.');
37187             return;
37188         }
37189         
37190         tagSize = tagType.size * length;
37191         // Determine if the value is contained in the dataOffset bytes,
37192         // or if the value at the dataOffset is a pointer to the actual data:
37193         dataOffset = tagSize > 4 ?
37194                 tiffOffset + dataView.getUint32(offset + 8, littleEndian) : (offset + 8);
37195         if (dataOffset + tagSize > dataView.byteLength) {
37196             Roo.log('Invalid Exif data: Invalid data offset.');
37197             return;
37198         }
37199         if (length === 1) {
37200             return tagType.getValue(dataView, dataOffset, littleEndian);
37201         }
37202         values = [];
37203         for (i = 0; i < length; i += 1) {
37204             values[i] = tagType.getValue(dataView, dataOffset + i * tagType.size, littleEndian);
37205         }
37206         
37207         if (tagType.ascii) {
37208             str = '';
37209             // Concatenate the chars:
37210             for (i = 0; i < values.length; i += 1) {
37211                 c = values[i];
37212                 // Ignore the terminating NULL byte(s):
37213                 if (c === '\u0000') {
37214                     break;
37215                 }
37216                 str += c;
37217             }
37218             return str;
37219         }
37220         return values;
37221     }
37222     
37223 });
37224
37225 Roo.apply(Roo.bootstrap.UploadCropbox, {
37226     tags : {
37227         'Orientation': 0x0112
37228     },
37229     
37230     Orientation: {
37231             1: 0, //'top-left',
37232 //            2: 'top-right',
37233             3: 180, //'bottom-right',
37234 //            4: 'bottom-left',
37235 //            5: 'left-top',
37236             6: 90, //'right-top',
37237 //            7: 'right-bottom',
37238             8: 270 //'left-bottom'
37239     },
37240     
37241     exifTagTypes : {
37242         // byte, 8-bit unsigned int:
37243         1: {
37244             getValue: function (dataView, dataOffset) {
37245                 return dataView.getUint8(dataOffset);
37246             },
37247             size: 1
37248         },
37249         // ascii, 8-bit byte:
37250         2: {
37251             getValue: function (dataView, dataOffset) {
37252                 return String.fromCharCode(dataView.getUint8(dataOffset));
37253             },
37254             size: 1,
37255             ascii: true
37256         },
37257         // short, 16 bit int:
37258         3: {
37259             getValue: function (dataView, dataOffset, littleEndian) {
37260                 return dataView.getUint16(dataOffset, littleEndian);
37261             },
37262             size: 2
37263         },
37264         // long, 32 bit int:
37265         4: {
37266             getValue: function (dataView, dataOffset, littleEndian) {
37267                 return dataView.getUint32(dataOffset, littleEndian);
37268             },
37269             size: 4
37270         },
37271         // rational = two long values, first is numerator, second is denominator:
37272         5: {
37273             getValue: function (dataView, dataOffset, littleEndian) {
37274                 return dataView.getUint32(dataOffset, littleEndian) /
37275                     dataView.getUint32(dataOffset + 4, littleEndian);
37276             },
37277             size: 8
37278         },
37279         // slong, 32 bit signed int:
37280         9: {
37281             getValue: function (dataView, dataOffset, littleEndian) {
37282                 return dataView.getInt32(dataOffset, littleEndian);
37283             },
37284             size: 4
37285         },
37286         // srational, two slongs, first is numerator, second is denominator:
37287         10: {
37288             getValue: function (dataView, dataOffset, littleEndian) {
37289                 return dataView.getInt32(dataOffset, littleEndian) /
37290                     dataView.getInt32(dataOffset + 4, littleEndian);
37291             },
37292             size: 8
37293         }
37294     },
37295     
37296     footer : {
37297         STANDARD : [
37298             {
37299                 tag : 'div',
37300                 cls : 'btn-group roo-upload-cropbox-rotate-left',
37301                 action : 'rotate-left',
37302                 cn : [
37303                     {
37304                         tag : 'button',
37305                         cls : 'btn btn-default',
37306                         html : '<i class="fa fa-undo"></i>'
37307                     }
37308                 ]
37309             },
37310             {
37311                 tag : 'div',
37312                 cls : 'btn-group roo-upload-cropbox-picture',
37313                 action : 'picture',
37314                 cn : [
37315                     {
37316                         tag : 'button',
37317                         cls : 'btn btn-default',
37318                         html : '<i class="fa fa-picture-o"></i>'
37319                     }
37320                 ]
37321             },
37322             {
37323                 tag : 'div',
37324                 cls : 'btn-group roo-upload-cropbox-rotate-right',
37325                 action : 'rotate-right',
37326                 cn : [
37327                     {
37328                         tag : 'button',
37329                         cls : 'btn btn-default',
37330                         html : '<i class="fa fa-repeat"></i>'
37331                     }
37332                 ]
37333             }
37334         ],
37335         DOCUMENT : [
37336             {
37337                 tag : 'div',
37338                 cls : 'btn-group roo-upload-cropbox-rotate-left',
37339                 action : 'rotate-left',
37340                 cn : [
37341                     {
37342                         tag : 'button',
37343                         cls : 'btn btn-default',
37344                         html : '<i class="fa fa-undo"></i>'
37345                     }
37346                 ]
37347             },
37348             {
37349                 tag : 'div',
37350                 cls : 'btn-group roo-upload-cropbox-download',
37351                 action : 'download',
37352                 cn : [
37353                     {
37354                         tag : 'button',
37355                         cls : 'btn btn-default',
37356                         html : '<i class="fa fa-download"></i>'
37357                     }
37358                 ]
37359             },
37360             {
37361                 tag : 'div',
37362                 cls : 'btn-group roo-upload-cropbox-crop',
37363                 action : 'crop',
37364                 cn : [
37365                     {
37366                         tag : 'button',
37367                         cls : 'btn btn-default',
37368                         html : '<i class="fa fa-crop"></i>'
37369                     }
37370                 ]
37371             },
37372             {
37373                 tag : 'div',
37374                 cls : 'btn-group roo-upload-cropbox-trash',
37375                 action : 'trash',
37376                 cn : [
37377                     {
37378                         tag : 'button',
37379                         cls : 'btn btn-default',
37380                         html : '<i class="fa fa-trash"></i>'
37381                     }
37382                 ]
37383             },
37384             {
37385                 tag : 'div',
37386                 cls : 'btn-group roo-upload-cropbox-rotate-right',
37387                 action : 'rotate-right',
37388                 cn : [
37389                     {
37390                         tag : 'button',
37391                         cls : 'btn btn-default',
37392                         html : '<i class="fa fa-repeat"></i>'
37393                     }
37394                 ]
37395             }
37396         ],
37397         ROTATOR : [
37398             {
37399                 tag : 'div',
37400                 cls : 'btn-group roo-upload-cropbox-rotate-left',
37401                 action : 'rotate-left',
37402                 cn : [
37403                     {
37404                         tag : 'button',
37405                         cls : 'btn btn-default',
37406                         html : '<i class="fa fa-undo"></i>'
37407                     }
37408                 ]
37409             },
37410             {
37411                 tag : 'div',
37412                 cls : 'btn-group roo-upload-cropbox-rotate-right',
37413                 action : 'rotate-right',
37414                 cn : [
37415                     {
37416                         tag : 'button',
37417                         cls : 'btn btn-default',
37418                         html : '<i class="fa fa-repeat"></i>'
37419                     }
37420                 ]
37421             }
37422         ]
37423     }
37424 });
37425
37426 /*
37427 * Licence: LGPL
37428 */
37429
37430 /**
37431  * @class Roo.bootstrap.DocumentManager
37432  * @extends Roo.bootstrap.Component
37433  * Bootstrap DocumentManager class
37434  * @cfg {String} paramName default 'imageUpload'
37435  * @cfg {String} toolTipName default 'filename'
37436  * @cfg {String} method default POST
37437  * @cfg {String} url action url
37438  * @cfg {Number} boxes number of boxes, 0 is no limit.. default 0
37439  * @cfg {Boolean} multiple multiple upload default true
37440  * @cfg {Number} thumbSize default 300
37441  * @cfg {String} fieldLabel
37442  * @cfg {Number} labelWidth default 4
37443  * @cfg {String} labelAlign (left|top) default left
37444  * @cfg {Boolean} editable (true|false) allow edit when upload a image default true
37445 * @cfg {Number} labellg set the width of label (1-12)
37446  * @cfg {Number} labelmd set the width of label (1-12)
37447  * @cfg {Number} labelsm set the width of label (1-12)
37448  * @cfg {Number} labelxs set the width of label (1-12)
37449  * 
37450  * @constructor
37451  * Create a new DocumentManager
37452  * @param {Object} config The config object
37453  */
37454
37455 Roo.bootstrap.DocumentManager = function(config){
37456     Roo.bootstrap.DocumentManager.superclass.constructor.call(this, config);
37457     
37458     this.files = [];
37459     this.delegates = [];
37460     
37461     this.addEvents({
37462         /**
37463          * @event initial
37464          * Fire when initial the DocumentManager
37465          * @param {Roo.bootstrap.DocumentManager} this
37466          */
37467         "initial" : true,
37468         /**
37469          * @event inspect
37470          * inspect selected file
37471          * @param {Roo.bootstrap.DocumentManager} this
37472          * @param {File} file
37473          */
37474         "inspect" : true,
37475         /**
37476          * @event exception
37477          * Fire when xhr load exception
37478          * @param {Roo.bootstrap.DocumentManager} this
37479          * @param {XMLHttpRequest} xhr
37480          */
37481         "exception" : true,
37482         /**
37483          * @event afterupload
37484          * Fire when xhr load exception
37485          * @param {Roo.bootstrap.DocumentManager} this
37486          * @param {XMLHttpRequest} xhr
37487          */
37488         "afterupload" : true,
37489         /**
37490          * @event prepare
37491          * prepare the form data
37492          * @param {Roo.bootstrap.DocumentManager} this
37493          * @param {Object} formData
37494          */
37495         "prepare" : true,
37496         /**
37497          * @event remove
37498          * Fire when remove the file
37499          * @param {Roo.bootstrap.DocumentManager} this
37500          * @param {Object} file
37501          */
37502         "remove" : true,
37503         /**
37504          * @event refresh
37505          * Fire after refresh the file
37506          * @param {Roo.bootstrap.DocumentManager} this
37507          */
37508         "refresh" : true,
37509         /**
37510          * @event click
37511          * Fire after click the image
37512          * @param {Roo.bootstrap.DocumentManager} this
37513          * @param {Object} file
37514          */
37515         "click" : true,
37516         /**
37517          * @event edit
37518          * Fire when upload a image and editable set to true
37519          * @param {Roo.bootstrap.DocumentManager} this
37520          * @param {Object} file
37521          */
37522         "edit" : true,
37523         /**
37524          * @event beforeselectfile
37525          * Fire before select file
37526          * @param {Roo.bootstrap.DocumentManager} this
37527          */
37528         "beforeselectfile" : true,
37529         /**
37530          * @event process
37531          * Fire before process file
37532          * @param {Roo.bootstrap.DocumentManager} this
37533          * @param {Object} file
37534          */
37535         "process" : true,
37536         /**
37537          * @event previewrendered
37538          * Fire when preview rendered
37539          * @param {Roo.bootstrap.DocumentManager} this
37540          * @param {Object} file
37541          */
37542         "previewrendered" : true,
37543         /**
37544          */
37545         "previewResize" : true
37546         
37547     });
37548 };
37549
37550 Roo.extend(Roo.bootstrap.DocumentManager, Roo.bootstrap.Component,  {
37551     
37552     boxes : 0,
37553     inputName : '',
37554     thumbSize : 300,
37555     multiple : true,
37556     files : false,
37557     method : 'POST',
37558     url : '',
37559     paramName : 'imageUpload',
37560     toolTipName : 'filename',
37561     fieldLabel : '',
37562     labelWidth : 4,
37563     labelAlign : 'left',
37564     editable : true,
37565     delegates : false,
37566     xhr : false, 
37567     
37568     labellg : 0,
37569     labelmd : 0,
37570     labelsm : 0,
37571     labelxs : 0,
37572     
37573     getAutoCreate : function()
37574     {   
37575         var managerWidget = {
37576             tag : 'div',
37577             cls : 'roo-document-manager',
37578             cn : [
37579                 {
37580                     tag : 'input',
37581                     cls : 'roo-document-manager-selector',
37582                     type : 'file'
37583                 },
37584                 {
37585                     tag : 'div',
37586                     cls : 'roo-document-manager-uploader',
37587                     cn : [
37588                         {
37589                             tag : 'div',
37590                             cls : 'roo-document-manager-upload-btn',
37591                             html : '<i class="fa fa-plus"></i>'
37592                         }
37593                     ]
37594                     
37595                 }
37596             ]
37597         };
37598         
37599         var content = [
37600             {
37601                 tag : 'div',
37602                 cls : 'column col-md-12',
37603                 cn : managerWidget
37604             }
37605         ];
37606         
37607         if(this.fieldLabel.length){
37608             
37609             content = [
37610                 {
37611                     tag : 'div',
37612                     cls : 'column col-md-12',
37613                     html : this.fieldLabel
37614                 },
37615                 {
37616                     tag : 'div',
37617                     cls : 'column col-md-12',
37618                     cn : managerWidget
37619                 }
37620             ];
37621
37622             if(this.labelAlign == 'left'){
37623                 content = [
37624                     {
37625                         tag : 'div',
37626                         cls : 'column',
37627                         html : this.fieldLabel
37628                     },
37629                     {
37630                         tag : 'div',
37631                         cls : 'column',
37632                         cn : managerWidget
37633                     }
37634                 ];
37635                 
37636                 if(this.labelWidth > 12){
37637                     content[0].style = "width: " + this.labelWidth + 'px';
37638                 }
37639
37640                 if(this.labelWidth < 13 && this.labelmd == 0){
37641                     this.labelmd = this.labelWidth;
37642                 }
37643
37644                 if(this.labellg > 0){
37645                     content[0].cls += ' col-lg-' + this.labellg;
37646                     content[1].cls += ' col-lg-' + (12 - this.labellg);
37647                 }
37648
37649                 if(this.labelmd > 0){
37650                     content[0].cls += ' col-md-' + this.labelmd;
37651                     content[1].cls += ' col-md-' + (12 - this.labelmd);
37652                 }
37653
37654                 if(this.labelsm > 0){
37655                     content[0].cls += ' col-sm-' + this.labelsm;
37656                     content[1].cls += ' col-sm-' + (12 - this.labelsm);
37657                 }
37658
37659                 if(this.labelxs > 0){
37660                     content[0].cls += ' col-xs-' + this.labelxs;
37661                     content[1].cls += ' col-xs-' + (12 - this.labelxs);
37662                 }
37663                 
37664             }
37665         }
37666         
37667         var cfg = {
37668             tag : 'div',
37669             cls : 'row clearfix',
37670             cn : content
37671         };
37672         
37673         return cfg;
37674         
37675     },
37676     
37677     initEvents : function()
37678     {
37679         this.managerEl = this.el.select('.roo-document-manager', true).first();
37680         this.managerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
37681         
37682         this.selectorEl = this.el.select('.roo-document-manager-selector', true).first();
37683         this.selectorEl.hide();
37684         
37685         if(this.multiple){
37686             this.selectorEl.attr('multiple', 'multiple');
37687         }
37688         
37689         this.selectorEl.on('change', this.onFileSelected, this);
37690         
37691         this.uploader = this.el.select('.roo-document-manager-uploader', true).first();
37692         this.uploader.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
37693         
37694         this.uploader.on('click', this.onUploaderClick, this);
37695         
37696         this.renderProgressDialog();
37697         
37698         var _this = this;
37699         
37700         window.addEventListener("resize", function() { _this.refresh(); } );
37701         
37702         this.fireEvent('initial', this);
37703     },
37704     
37705     renderProgressDialog : function()
37706     {
37707         var _this = this;
37708         
37709         this.progressDialog = new Roo.bootstrap.Modal({
37710             cls : 'roo-document-manager-progress-dialog',
37711             allow_close : false,
37712             animate : false,
37713             title : '',
37714             buttons : [
37715                 {
37716                     name  :'cancel',
37717                     weight : 'danger',
37718                     html : 'Cancel'
37719                 }
37720             ], 
37721             listeners : { 
37722                 btnclick : function() {
37723                     _this.uploadCancel();
37724                     this.hide();
37725                 }
37726             }
37727         });
37728          
37729         this.progressDialog.render(Roo.get(document.body));
37730          
37731         this.progress = new Roo.bootstrap.Progress({
37732             cls : 'roo-document-manager-progress',
37733             active : true,
37734             striped : true
37735         });
37736         
37737         this.progress.render(this.progressDialog.getChildContainer());
37738         
37739         this.progressBar = new Roo.bootstrap.ProgressBar({
37740             cls : 'roo-document-manager-progress-bar',
37741             aria_valuenow : 0,
37742             aria_valuemin : 0,
37743             aria_valuemax : 12,
37744             panel : 'success'
37745         });
37746         
37747         this.progressBar.render(this.progress.getChildContainer());
37748     },
37749     
37750     onUploaderClick : function(e)
37751     {
37752         e.preventDefault();
37753      
37754         if(this.fireEvent('beforeselectfile', this) != false){
37755             this.selectorEl.dom.click();
37756         }
37757         
37758     },
37759     
37760     onFileSelected : function(e)
37761     {
37762         e.preventDefault();
37763         
37764         if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
37765             return;
37766         }
37767         
37768         Roo.each(this.selectorEl.dom.files, function(file){
37769             if(this.fireEvent('inspect', this, file) != false){
37770                 this.files.push(file);
37771             }
37772         }, this);
37773         
37774         this.queue();
37775         
37776     },
37777     
37778     queue : function()
37779     {
37780         this.selectorEl.dom.value = '';
37781         
37782         if(!this.files || !this.files.length){
37783             return;
37784         }
37785         
37786         if(this.boxes > 0 && this.files.length > this.boxes){
37787             this.files = this.files.slice(0, this.boxes);
37788         }
37789         
37790         this.uploader.show();
37791         
37792         if(this.boxes > 0 && this.files.length > this.boxes - 1){
37793             this.uploader.hide();
37794         }
37795         
37796         var _this = this;
37797         
37798         var files = [];
37799         
37800         var docs = [];
37801         
37802         Roo.each(this.files, function(file){
37803             
37804             if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
37805                 var f = this.renderPreview(file);
37806                 files.push(f);
37807                 return;
37808             }
37809             
37810             if(file.type.indexOf('image') != -1){
37811                 this.delegates.push(
37812                     (function(){
37813                         _this.process(file);
37814                     }).createDelegate(this)
37815                 );
37816         
37817                 return;
37818             }
37819             
37820             docs.push(
37821                 (function(){
37822                     _this.process(file);
37823                 }).createDelegate(this)
37824             );
37825             
37826         }, this);
37827         
37828         this.files = files;
37829         
37830         this.delegates = this.delegates.concat(docs);
37831         
37832         if(!this.delegates.length){
37833             this.refresh();
37834             return;
37835         }
37836         
37837         this.progressBar.aria_valuemax = this.delegates.length;
37838         
37839         this.arrange();
37840         
37841         return;
37842     },
37843     
37844     arrange : function()
37845     {
37846         if(!this.delegates.length){
37847             this.progressDialog.hide();
37848             this.refresh();
37849             return;
37850         }
37851         
37852         var delegate = this.delegates.shift();
37853         
37854         this.progressDialog.show();
37855         
37856         this.progressDialog.setTitle((this.progressBar.aria_valuemax - this.delegates.length) + ' / ' + this.progressBar.aria_valuemax);
37857         
37858         this.progressBar.update(this.progressBar.aria_valuemax - this.delegates.length);
37859         
37860         delegate();
37861     },
37862     
37863     refresh : function()
37864     {
37865         this.uploader.show();
37866         
37867         if(this.boxes > 0 && this.files.length > this.boxes - 1){
37868             this.uploader.hide();
37869         }
37870         
37871         Roo.isTouch ? this.closable(false) : this.closable(true);
37872         
37873         this.fireEvent('refresh', this);
37874     },
37875     
37876     onRemove : function(e, el, o)
37877     {
37878         e.preventDefault();
37879         
37880         this.fireEvent('remove', this, o);
37881         
37882     },
37883     
37884     remove : function(o)
37885     {
37886         var files = [];
37887         
37888         Roo.each(this.files, function(file){
37889             if(typeof(file.id) == 'undefined' || file.id * 1 < 1 || file.id != o.id){
37890                 files.push(file);
37891                 return;
37892             }
37893
37894             o.target.remove();
37895
37896         }, this);
37897         
37898         this.files = files;
37899         
37900         this.refresh();
37901     },
37902     
37903     clear : function()
37904     {
37905         Roo.each(this.files, function(file){
37906             if(!file.target){
37907                 return;
37908             }
37909             
37910             file.target.remove();
37911
37912         }, this);
37913         
37914         this.files = [];
37915         
37916         this.refresh();
37917     },
37918     
37919     onClick : function(e, el, o)
37920     {
37921         e.preventDefault();
37922         
37923         this.fireEvent('click', this, o);
37924         
37925     },
37926     
37927     closable : function(closable)
37928     {
37929         Roo.each(this.managerEl.select('.roo-document-manager-preview > button.close', true).elements, function(el){
37930             
37931             el.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
37932             
37933             if(closable){
37934                 el.show();
37935                 return;
37936             }
37937             
37938             el.hide();
37939             
37940         }, this);
37941     },
37942     
37943     xhrOnLoad : function(xhr)
37944     {
37945         Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
37946             el.remove();
37947         }, this);
37948         
37949         if (xhr.readyState !== 4) {
37950             this.arrange();
37951             this.fireEvent('exception', this, xhr);
37952             return;
37953         }
37954
37955         var response = Roo.decode(xhr.responseText);
37956         
37957         if(!response.success){
37958             this.arrange();
37959             this.fireEvent('exception', this, xhr);
37960             return;
37961         }
37962         
37963         var file = this.renderPreview(response.data);
37964         
37965         this.files.push(file);
37966         
37967         this.arrange();
37968         
37969         this.fireEvent('afterupload', this, xhr);
37970         
37971     },
37972     
37973     xhrOnError : function(xhr)
37974     {
37975         Roo.log('xhr on error');
37976         
37977         var response = Roo.decode(xhr.responseText);
37978           
37979         Roo.log(response);
37980         
37981         this.arrange();
37982     },
37983     
37984     process : function(file)
37985     {
37986         if(this.fireEvent('process', this, file) !== false){
37987             if(this.editable && file.type.indexOf('image') != -1){
37988                 this.fireEvent('edit', this, file);
37989                 return;
37990             }
37991
37992             this.uploadStart(file, false);
37993
37994             return;
37995         }
37996         
37997     },
37998     
37999     uploadStart : function(file, crop)
38000     {
38001         this.xhr = new XMLHttpRequest();
38002         
38003         if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
38004             this.arrange();
38005             return;
38006         }
38007         
38008         file.xhr = this.xhr;
38009             
38010         this.managerEl.createChild({
38011             tag : 'div',
38012             cls : 'roo-document-manager-loading',
38013             cn : [
38014                 {
38015                     tag : 'div',
38016                     tooltip : file.name,
38017                     cls : 'roo-document-manager-thumb',
38018                     html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
38019                 }
38020             ]
38021
38022         });
38023
38024         this.xhr.open(this.method, this.url, true);
38025         
38026         var headers = {
38027             "Accept": "application/json",
38028             "Cache-Control": "no-cache",
38029             "X-Requested-With": "XMLHttpRequest"
38030         };
38031         
38032         for (var headerName in headers) {
38033             var headerValue = headers[headerName];
38034             if (headerValue) {
38035                 this.xhr.setRequestHeader(headerName, headerValue);
38036             }
38037         }
38038         
38039         var _this = this;
38040         
38041         this.xhr.onload = function()
38042         {
38043             _this.xhrOnLoad(_this.xhr);
38044         }
38045         
38046         this.xhr.onerror = function()
38047         {
38048             _this.xhrOnError(_this.xhr);
38049         }
38050         
38051         var formData = new FormData();
38052
38053         formData.append('returnHTML', 'NO');
38054         
38055         if(crop){
38056             formData.append('crop', crop);
38057         }
38058         
38059         formData.append(this.paramName, file, file.name);
38060         
38061         var options = {
38062             file : file, 
38063             manually : false
38064         };
38065         
38066         if(this.fireEvent('prepare', this, formData, options) != false){
38067             
38068             if(options.manually){
38069                 return;
38070             }
38071             
38072             this.xhr.send(formData);
38073             return;
38074         };
38075         
38076         this.uploadCancel();
38077     },
38078     
38079     uploadCancel : function()
38080     {
38081         if (this.xhr) {
38082             this.xhr.abort();
38083         }
38084         
38085         this.delegates = [];
38086         
38087         Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
38088             el.remove();
38089         }, this);
38090         
38091         this.arrange();
38092     },
38093     
38094     renderPreview : function(file)
38095     {
38096         if(typeof(file.target) != 'undefined' && file.target){
38097             return file;
38098         }
38099         
38100         var img_src = encodeURI(baseURL +'/Images/Thumb/' + this.thumbSize + '/' + file.id + '/' + file.filename);
38101         
38102         var previewEl = this.managerEl.createChild({
38103             tag : 'div',
38104             cls : 'roo-document-manager-preview',
38105             cn : [
38106                 {
38107                     tag : 'div',
38108                     tooltip : file[this.toolTipName],
38109                     cls : 'roo-document-manager-thumb',
38110                     html : '<img tooltip="' + file[this.toolTipName] + '" src="' + img_src + '">'
38111                 },
38112                 {
38113                     tag : 'button',
38114                     cls : 'close',
38115                     html : '<i class="fa fa-times-circle"></i>'
38116                 }
38117             ]
38118         });
38119
38120         var close = previewEl.select('button.close', true).first();
38121
38122         close.on('click', this.onRemove, this, file);
38123
38124         file.target = previewEl;
38125
38126         var image = previewEl.select('img', true).first();
38127         
38128         var _this = this;
38129         
38130         image.dom.addEventListener("load", function(){ _this.onPreviewLoad(file, image); });
38131         
38132         image.on('click', this.onClick, this, file);
38133         
38134         this.fireEvent('previewrendered', this, file);
38135         
38136         return file;
38137         
38138     },
38139     
38140     onPreviewLoad : function(file, image)
38141     {
38142         if(typeof(file.target) == 'undefined' || !file.target){
38143             return;
38144         }
38145         
38146         var width = image.dom.naturalWidth || image.dom.width;
38147         var height = image.dom.naturalHeight || image.dom.height;
38148         
38149         if(!this.previewResize) {
38150             return;
38151         }
38152         
38153         if(width > height){
38154             file.target.addClass('wide');
38155             return;
38156         }
38157         
38158         file.target.addClass('tall');
38159         return;
38160         
38161     },
38162     
38163     uploadFromSource : function(file, crop)
38164     {
38165         this.xhr = new XMLHttpRequest();
38166         
38167         this.managerEl.createChild({
38168             tag : 'div',
38169             cls : 'roo-document-manager-loading',
38170             cn : [
38171                 {
38172                     tag : 'div',
38173                     tooltip : file.name,
38174                     cls : 'roo-document-manager-thumb',
38175                     html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
38176                 }
38177             ]
38178
38179         });
38180
38181         this.xhr.open(this.method, this.url, true);
38182         
38183         var headers = {
38184             "Accept": "application/json",
38185             "Cache-Control": "no-cache",
38186             "X-Requested-With": "XMLHttpRequest"
38187         };
38188         
38189         for (var headerName in headers) {
38190             var headerValue = headers[headerName];
38191             if (headerValue) {
38192                 this.xhr.setRequestHeader(headerName, headerValue);
38193             }
38194         }
38195         
38196         var _this = this;
38197         
38198         this.xhr.onload = function()
38199         {
38200             _this.xhrOnLoad(_this.xhr);
38201         }
38202         
38203         this.xhr.onerror = function()
38204         {
38205             _this.xhrOnError(_this.xhr);
38206         }
38207         
38208         var formData = new FormData();
38209
38210         formData.append('returnHTML', 'NO');
38211         
38212         formData.append('crop', crop);
38213         
38214         if(typeof(file.filename) != 'undefined'){
38215             formData.append('filename', file.filename);
38216         }
38217         
38218         if(typeof(file.mimetype) != 'undefined'){
38219             formData.append('mimetype', file.mimetype);
38220         }
38221         
38222         Roo.log(formData);
38223         
38224         if(this.fireEvent('prepare', this, formData) != false){
38225             this.xhr.send(formData);
38226         };
38227     }
38228 });
38229
38230 /*
38231 * Licence: LGPL
38232 */
38233
38234 /**
38235  * @class Roo.bootstrap.DocumentViewer
38236  * @extends Roo.bootstrap.Component
38237  * Bootstrap DocumentViewer class
38238  * @cfg {Boolean} showDownload (true|false) show download button (default true)
38239  * @cfg {Boolean} showTrash (true|false) show trash button (default true)
38240  * 
38241  * @constructor
38242  * Create a new DocumentViewer
38243  * @param {Object} config The config object
38244  */
38245
38246 Roo.bootstrap.DocumentViewer = function(config){
38247     Roo.bootstrap.DocumentViewer.superclass.constructor.call(this, config);
38248     
38249     this.addEvents({
38250         /**
38251          * @event initial
38252          * Fire after initEvent
38253          * @param {Roo.bootstrap.DocumentViewer} this
38254          */
38255         "initial" : true,
38256         /**
38257          * @event click
38258          * Fire after click
38259          * @param {Roo.bootstrap.DocumentViewer} this
38260          */
38261         "click" : true,
38262         /**
38263          * @event download
38264          * Fire after download button
38265          * @param {Roo.bootstrap.DocumentViewer} this
38266          */
38267         "download" : true,
38268         /**
38269          * @event trash
38270          * Fire after trash button
38271          * @param {Roo.bootstrap.DocumentViewer} this
38272          */
38273         "trash" : true
38274         
38275     });
38276 };
38277
38278 Roo.extend(Roo.bootstrap.DocumentViewer, Roo.bootstrap.Component,  {
38279     
38280     showDownload : true,
38281     
38282     showTrash : true,
38283     
38284     getAutoCreate : function()
38285     {
38286         var cfg = {
38287             tag : 'div',
38288             cls : 'roo-document-viewer',
38289             cn : [
38290                 {
38291                     tag : 'div',
38292                     cls : 'roo-document-viewer-body',
38293                     cn : [
38294                         {
38295                             tag : 'div',
38296                             cls : 'roo-document-viewer-thumb',
38297                             cn : [
38298                                 {
38299                                     tag : 'img',
38300                                     cls : 'roo-document-viewer-image'
38301                                 }
38302                             ]
38303                         }
38304                     ]
38305                 },
38306                 {
38307                     tag : 'div',
38308                     cls : 'roo-document-viewer-footer',
38309                     cn : {
38310                         tag : 'div',
38311                         cls : 'btn-group btn-group-justified roo-document-viewer-btn-group',
38312                         cn : [
38313                             {
38314                                 tag : 'div',
38315                                 cls : 'btn-group roo-document-viewer-download',
38316                                 cn : [
38317                                     {
38318                                         tag : 'button',
38319                                         cls : 'btn btn-default',
38320                                         html : '<i class="fa fa-download"></i>'
38321                                     }
38322                                 ]
38323                             },
38324                             {
38325                                 tag : 'div',
38326                                 cls : 'btn-group roo-document-viewer-trash',
38327                                 cn : [
38328                                     {
38329                                         tag : 'button',
38330                                         cls : 'btn btn-default',
38331                                         html : '<i class="fa fa-trash"></i>'
38332                                     }
38333                                 ]
38334                             }
38335                         ]
38336                     }
38337                 }
38338             ]
38339         };
38340         
38341         return cfg;
38342     },
38343     
38344     initEvents : function()
38345     {
38346         this.bodyEl = this.el.select('.roo-document-viewer-body', true).first();
38347         this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY);
38348         
38349         this.thumbEl = this.el.select('.roo-document-viewer-thumb', true).first();
38350         this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY);
38351         
38352         this.imageEl = this.el.select('.roo-document-viewer-image', true).first();
38353         this.imageEl.setVisibilityMode(Roo.Element.DISPLAY);
38354         
38355         this.footerEl = this.el.select('.roo-document-viewer-footer', true).first();
38356         this.footerEl.setVisibilityMode(Roo.Element.DISPLAY);
38357         
38358         this.downloadBtn = this.el.select('.roo-document-viewer-download', true).first();
38359         this.downloadBtn.setVisibilityMode(Roo.Element.DISPLAY);
38360         
38361         this.trashBtn = this.el.select('.roo-document-viewer-trash', true).first();
38362         this.trashBtn.setVisibilityMode(Roo.Element.DISPLAY);
38363         
38364         this.bodyEl.on('click', this.onClick, this);
38365         this.downloadBtn.on('click', this.onDownload, this);
38366         this.trashBtn.on('click', this.onTrash, this);
38367         
38368         this.downloadBtn.hide();
38369         this.trashBtn.hide();
38370         
38371         if(this.showDownload){
38372             this.downloadBtn.show();
38373         }
38374         
38375         if(this.showTrash){
38376             this.trashBtn.show();
38377         }
38378         
38379         if(!this.showDownload && !this.showTrash) {
38380             this.footerEl.hide();
38381         }
38382         
38383     },
38384     
38385     initial : function()
38386     {
38387         this.fireEvent('initial', this);
38388         
38389     },
38390     
38391     onClick : function(e)
38392     {
38393         e.preventDefault();
38394         
38395         this.fireEvent('click', this);
38396     },
38397     
38398     onDownload : function(e)
38399     {
38400         e.preventDefault();
38401         
38402         this.fireEvent('download', this);
38403     },
38404     
38405     onTrash : function(e)
38406     {
38407         e.preventDefault();
38408         
38409         this.fireEvent('trash', this);
38410     }
38411     
38412 });
38413 /*
38414  * - LGPL
38415  *
38416  * FieldLabel
38417  * 
38418  */
38419
38420 /**
38421  * @class Roo.bootstrap.form.FieldLabel
38422  * @extends Roo.bootstrap.Component
38423  * Bootstrap FieldLabel class
38424  * @cfg {String} html contents of the element
38425  * @cfg {String} tag tag of the element default label
38426  * @cfg {String} cls class of the element
38427  * @cfg {String} target label target 
38428  * @cfg {Boolean} allowBlank (true|false) target allowBlank default true
38429  * @cfg {String} invalidClass DEPRICATED - BS4 uses is-invalid
38430  * @cfg {String} validClass DEPRICATED - BS4 uses is-valid
38431  * @cfg {String} iconTooltip default "This field is required"
38432  * @cfg {String} indicatorpos (left|right) default left
38433  * 
38434  * @constructor
38435  * Create a new FieldLabel
38436  * @param {Object} config The config object
38437  */
38438
38439 Roo.bootstrap.form.FieldLabel = function(config){
38440     Roo.bootstrap.Element.superclass.constructor.call(this, config);
38441     
38442     this.addEvents({
38443             /**
38444              * @event invalid
38445              * Fires after the field has been marked as invalid.
38446              * @param {Roo.form.FieldLabel} this
38447              * @param {String} msg The validation message
38448              */
38449             invalid : true,
38450             /**
38451              * @event valid
38452              * Fires after the field has been validated with no errors.
38453              * @param {Roo.form.FieldLabel} this
38454              */
38455             valid : true
38456         });
38457 };
38458
38459 Roo.extend(Roo.bootstrap.form.FieldLabel, Roo.bootstrap.Component,  {
38460     
38461     tag: 'label',
38462     cls: '',
38463     html: '',
38464     target: '',
38465     allowBlank : true,
38466     invalidClass : 'has-warning',
38467     validClass : 'has-success',
38468     iconTooltip : 'This field is required',
38469     indicatorpos : 'left',
38470     
38471     getAutoCreate : function(){
38472         
38473         var cls = "";
38474         if (!this.allowBlank) {
38475             cls  = "visible";
38476         }
38477         
38478         var cfg = {
38479             tag : this.tag,
38480             cls : 'roo-bootstrap-field-label ' + this.cls,
38481             for : this.target,
38482             cn : [
38483                 {
38484                     tag : 'i',
38485                     cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star ' + cls,
38486                     tooltip : this.iconTooltip
38487                 },
38488                 {
38489                     tag : 'span',
38490                     html : this.html
38491                 }
38492             ] 
38493         };
38494         
38495         if(this.indicatorpos == 'right'){
38496             var cfg = {
38497                 tag : this.tag,
38498                 cls : 'roo-bootstrap-field-label ' + this.cls,
38499                 for : this.target,
38500                 cn : [
38501                     {
38502                         tag : 'span',
38503                         html : this.html
38504                     },
38505                     {
38506                         tag : 'i',
38507                         cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star '+ cls,
38508                         tooltip : this.iconTooltip
38509                     }
38510                 ] 
38511             };
38512         }
38513         
38514         return cfg;
38515     },
38516     
38517     initEvents: function() 
38518     {
38519         Roo.bootstrap.Element.superclass.initEvents.call(this);
38520         
38521         this.indicator = this.indicatorEl();
38522         
38523         if(this.indicator){
38524             this.indicator.removeClass('visible');
38525             this.indicator.addClass('invisible');
38526         }
38527         
38528         Roo.bootstrap.form.FieldLabel.register(this);
38529     },
38530     
38531     indicatorEl : function()
38532     {
38533         var indicator = this.el.select('i.roo-required-indicator',true).first();
38534         
38535         if(!indicator){
38536             return false;
38537         }
38538         
38539         return indicator;
38540         
38541     },
38542     
38543     /**
38544      * Mark this field as valid
38545      */
38546     markValid : function()
38547     {
38548         if(this.indicator){
38549             this.indicator.removeClass('visible');
38550             this.indicator.addClass('invisible');
38551         }
38552         if (Roo.bootstrap.version == 3) {
38553             this.el.removeClass(this.invalidClass);
38554             this.el.addClass(this.validClass);
38555         } else {
38556             this.el.removeClass('is-invalid');
38557             this.el.addClass('is-valid');
38558         }
38559         
38560         
38561         this.fireEvent('valid', this);
38562     },
38563     
38564     /**
38565      * Mark this field as invalid
38566      * @param {String} msg The validation message
38567      */
38568     markInvalid : function(msg)
38569     {
38570         if(this.indicator){
38571             this.indicator.removeClass('invisible');
38572             this.indicator.addClass('visible');
38573         }
38574           if (Roo.bootstrap.version == 3) {
38575             this.el.removeClass(this.validClass);
38576             this.el.addClass(this.invalidClass);
38577         } else {
38578             this.el.removeClass('is-valid');
38579             this.el.addClass('is-invalid');
38580         }
38581         
38582         
38583         this.fireEvent('invalid', this, msg);
38584     }
38585     
38586    
38587 });
38588
38589 Roo.apply(Roo.bootstrap.form.FieldLabel, {
38590     
38591     groups: {},
38592     
38593      /**
38594     * register a FieldLabel Group
38595     * @param {Roo.bootstrap.form.FieldLabel} the FieldLabel to add
38596     */
38597     register : function(label)
38598     {
38599         if(this.groups.hasOwnProperty(label.target)){
38600             return;
38601         }
38602      
38603         this.groups[label.target] = label;
38604         
38605     },
38606     /**
38607     * fetch a FieldLabel Group based on the target
38608     * @param {string} target
38609     * @returns {Roo.bootstrap.form.FieldLabel} the CheckBox group
38610     */
38611     get: function(target) {
38612         if (typeof(this.groups[target]) == 'undefined') {
38613             return false;
38614         }
38615         
38616         return this.groups[target] ;
38617     }
38618 });
38619
38620  
38621
38622  /*
38623  * - LGPL
38624  *
38625  * page DateSplitField.
38626  * 
38627  */
38628
38629
38630 /**
38631  * @class Roo.bootstrap.form.DateSplitField
38632  * @extends Roo.bootstrap.Component
38633  * Bootstrap DateSplitField class
38634  * @cfg {string} fieldLabel - the label associated
38635  * @cfg {Number} labelWidth set the width of label (0-12)
38636  * @cfg {String} labelAlign (top|left)
38637  * @cfg {Boolean} dayAllowBlank (true|false) default false
38638  * @cfg {Boolean} monthAllowBlank (true|false) default false
38639  * @cfg {Boolean} yearAllowBlank (true|false) default false
38640  * @cfg {string} dayPlaceholder 
38641  * @cfg {string} monthPlaceholder
38642  * @cfg {string} yearPlaceholder
38643  * @cfg {string} dayFormat default 'd'
38644  * @cfg {string} monthFormat default 'm'
38645  * @cfg {string} yearFormat default 'Y'
38646  * @cfg {Number} labellg set the width of label (1-12)
38647  * @cfg {Number} labelmd set the width of label (1-12)
38648  * @cfg {Number} labelsm set the width of label (1-12)
38649  * @cfg {Number} labelxs set the width of label (1-12)
38650
38651  *     
38652  * @constructor
38653  * Create a new DateSplitField
38654  * @param {Object} config The config object
38655  */
38656
38657 Roo.bootstrap.form.DateSplitField = function(config){
38658     Roo.bootstrap.form.DateSplitField.superclass.constructor.call(this, config);
38659     
38660     this.addEvents({
38661         // raw events
38662          /**
38663          * @event years
38664          * getting the data of years
38665          * @param {Roo.bootstrap.form.DateSplitField} this
38666          * @param {Object} years
38667          */
38668         "years" : true,
38669         /**
38670          * @event days
38671          * getting the data of days
38672          * @param {Roo.bootstrap.form.DateSplitField} this
38673          * @param {Object} days
38674          */
38675         "days" : true,
38676         /**
38677          * @event invalid
38678          * Fires after the field has been marked as invalid.
38679          * @param {Roo.form.Field} this
38680          * @param {String} msg The validation message
38681          */
38682         invalid : true,
38683        /**
38684          * @event valid
38685          * Fires after the field has been validated with no errors.
38686          * @param {Roo.form.Field} this
38687          */
38688         valid : true
38689     });
38690 };
38691
38692 Roo.extend(Roo.bootstrap.form.DateSplitField, Roo.bootstrap.Component,  {
38693     
38694     fieldLabel : '',
38695     labelAlign : 'top',
38696     labelWidth : 3,
38697     dayAllowBlank : false,
38698     monthAllowBlank : false,
38699     yearAllowBlank : false,
38700     dayPlaceholder : '',
38701     monthPlaceholder : '',
38702     yearPlaceholder : '',
38703     dayFormat : 'd',
38704     monthFormat : 'm',
38705     yearFormat : 'Y',
38706     isFormField : true,
38707     labellg : 0,
38708     labelmd : 0,
38709     labelsm : 0,
38710     labelxs : 0,
38711     
38712     getAutoCreate : function()
38713     {
38714         var cfg = {
38715             tag : 'div',
38716             cls : 'row roo-date-split-field-group',
38717             cn : [
38718                 {
38719                     tag : 'input',
38720                     type : 'hidden',
38721                     cls : 'form-hidden-field roo-date-split-field-group-value',
38722                     name : this.name
38723                 }
38724             ]
38725         };
38726         
38727         var labelCls = 'col-md-12';
38728         var contentCls = 'col-md-4';
38729         
38730         if(this.fieldLabel){
38731             
38732             var label = {
38733                 tag : 'div',
38734                 cls : 'column roo-date-split-field-label col-md-' + ((this.labelAlign == 'top') ? '12' : this.labelWidth),
38735                 cn : [
38736                     {
38737                         tag : 'label',
38738                         html : this.fieldLabel
38739                     }
38740                 ]
38741             };
38742             
38743             if(this.labelAlign == 'left'){
38744             
38745                 if(this.labelWidth > 12){
38746                     label.style = "width: " + this.labelWidth + 'px';
38747                 }
38748
38749                 if(this.labelWidth < 13 && this.labelmd == 0){
38750                     this.labelmd = this.labelWidth;
38751                 }
38752
38753                 if(this.labellg > 0){
38754                     labelCls = ' col-lg-' + this.labellg;
38755                     contentCls = ' col-lg-' + ((12 - this.labellg) / 3);
38756                 }
38757
38758                 if(this.labelmd > 0){
38759                     labelCls = ' col-md-' + this.labelmd;
38760                     contentCls = ' col-md-' + ((12 - this.labelmd) / 3);
38761                 }
38762
38763                 if(this.labelsm > 0){
38764                     labelCls = ' col-sm-' + this.labelsm;
38765                     contentCls = ' col-sm-' + ((12 - this.labelsm) / 3);
38766                 }
38767
38768                 if(this.labelxs > 0){
38769                     labelCls = ' col-xs-' + this.labelxs;
38770                     contentCls = ' col-xs-' + ((12 - this.labelxs) / 3);
38771                 }
38772             }
38773             
38774             label.cls += ' ' + labelCls;
38775             
38776             cfg.cn.push(label);
38777         }
38778         
38779         Roo.each(['day', 'month', 'year'], function(t){
38780             cfg.cn.push({
38781                 tag : 'div',
38782                 cls : 'column roo-date-split-field-' + t + ' ' + contentCls
38783             });
38784         }, this);
38785         
38786         return cfg;
38787     },
38788     
38789     inputEl: function ()
38790     {
38791         return this.el.select('.roo-date-split-field-group-value', true).first();
38792     },
38793     
38794     onRender : function(ct, position) 
38795     {
38796         var _this = this;
38797         
38798         Roo.bootstrap.DateSplitFiel.superclass.onRender.call(this, ct, position);
38799         
38800         this.inputEl = this.el.select('.roo-date-split-field-group-value', true).first();
38801         
38802         this.dayField = new Roo.bootstrap.form.ComboBox({
38803             allowBlank : this.dayAllowBlank,
38804             alwaysQuery : true,
38805             displayField : 'value',
38806             editable : false,
38807             fieldLabel : '',
38808             forceSelection : true,
38809             mode : 'local',
38810             placeholder : this.dayPlaceholder,
38811             selectOnFocus : true,
38812             tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
38813             triggerAction : 'all',
38814             typeAhead : true,
38815             valueField : 'value',
38816             store : new Roo.data.SimpleStore({
38817                 data : (function() {    
38818                     var days = [];
38819                     _this.fireEvent('days', _this, days);
38820                     return days;
38821                 })(),
38822                 fields : [ 'value' ]
38823             }),
38824             listeners : {
38825                 select : function (_self, record, index)
38826                 {
38827                     _this.setValue(_this.getValue());
38828                 }
38829             }
38830         });
38831
38832         this.dayField.render(this.el.select('.roo-date-split-field-day', true).first(), null);
38833         
38834         this.monthField = new Roo.bootstrap.form.MonthField({
38835             after : '<i class=\"fa fa-calendar\"></i>',
38836             allowBlank : this.monthAllowBlank,
38837             placeholder : this.monthPlaceholder,
38838             readOnly : true,
38839             listeners : {
38840                 render : function (_self)
38841                 {
38842                     this.el.select('span.input-group-addon', true).first().on('click', function(e){
38843                         e.preventDefault();
38844                         _self.focus();
38845                     });
38846                 },
38847                 select : function (_self, oldvalue, newvalue)
38848                 {
38849                     _this.setValue(_this.getValue());
38850                 }
38851             }
38852         });
38853         
38854         this.monthField.render(this.el.select('.roo-date-split-field-month', true).first(), null);
38855         
38856         this.yearField = new Roo.bootstrap.form.ComboBox({
38857             allowBlank : this.yearAllowBlank,
38858             alwaysQuery : true,
38859             displayField : 'value',
38860             editable : false,
38861             fieldLabel : '',
38862             forceSelection : true,
38863             mode : 'local',
38864             placeholder : this.yearPlaceholder,
38865             selectOnFocus : true,
38866             tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
38867             triggerAction : 'all',
38868             typeAhead : true,
38869             valueField : 'value',
38870             store : new Roo.data.SimpleStore({
38871                 data : (function() {
38872                     var years = [];
38873                     _this.fireEvent('years', _this, years);
38874                     return years;
38875                 })(),
38876                 fields : [ 'value' ]
38877             }),
38878             listeners : {
38879                 select : function (_self, record, index)
38880                 {
38881                     _this.setValue(_this.getValue());
38882                 }
38883             }
38884         });
38885
38886         this.yearField.render(this.el.select('.roo-date-split-field-year', true).first(), null);
38887     },
38888     
38889     setValue : function(v, format)
38890     {
38891         this.inputEl.dom.value = v;
38892         
38893         var f = format || (this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat);
38894         
38895         var d = Date.parseDate(v, f);
38896         
38897         if(!d){
38898             this.validate();
38899             return;
38900         }
38901         
38902         this.setDay(d.format(this.dayFormat));
38903         this.setMonth(d.format(this.monthFormat));
38904         this.setYear(d.format(this.yearFormat));
38905         
38906         this.validate();
38907         
38908         return;
38909     },
38910     
38911     setDay : function(v)
38912     {
38913         this.dayField.setValue(v);
38914         this.inputEl.dom.value = this.getValue();
38915         this.validate();
38916         return;
38917     },
38918     
38919     setMonth : function(v)
38920     {
38921         this.monthField.setValue(v, true);
38922         this.inputEl.dom.value = this.getValue();
38923         this.validate();
38924         return;
38925     },
38926     
38927     setYear : function(v)
38928     {
38929         this.yearField.setValue(v);
38930         this.inputEl.dom.value = this.getValue();
38931         this.validate();
38932         return;
38933     },
38934     
38935     getDay : function()
38936     {
38937         return this.dayField.getValue();
38938     },
38939     
38940     getMonth : function()
38941     {
38942         return this.monthField.getValue();
38943     },
38944     
38945     getYear : function()
38946     {
38947         return this.yearField.getValue();
38948     },
38949     
38950     getValue : function()
38951     {
38952         var f = this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat;
38953         
38954         var date = this.yearField.getValue() + '-' + this.monthField.getValue() + '-' + this.dayField.getValue();
38955         
38956         return date;
38957     },
38958     
38959     reset : function()
38960     {
38961         this.setDay('');
38962         this.setMonth('');
38963         this.setYear('');
38964         this.inputEl.dom.value = '';
38965         this.validate();
38966         return;
38967     },
38968     
38969     validate : function()
38970     {
38971         var d = this.dayField.validate();
38972         var m = this.monthField.validate();
38973         var y = this.yearField.validate();
38974         
38975         var valid = true;
38976         
38977         if(
38978                 (!this.dayAllowBlank && !d) ||
38979                 (!this.monthAllowBlank && !m) ||
38980                 (!this.yearAllowBlank && !y)
38981         ){
38982             valid = false;
38983         }
38984         
38985         if(this.dayAllowBlank && this.monthAllowBlank && this.yearAllowBlank){
38986             return valid;
38987         }
38988         
38989         if(valid){
38990             this.markValid();
38991             return valid;
38992         }
38993         
38994         this.markInvalid();
38995         
38996         return valid;
38997     },
38998     
38999     markValid : function()
39000     {
39001         
39002         var label = this.el.select('label', true).first();
39003         var icon = this.el.select('i.fa-star', true).first();
39004
39005         if(label && icon){
39006             icon.remove();
39007         }
39008         
39009         this.fireEvent('valid', this);
39010     },
39011     
39012      /**
39013      * Mark this field as invalid
39014      * @param {String} msg The validation message
39015      */
39016     markInvalid : function(msg)
39017     {
39018         
39019         var label = this.el.select('label', true).first();
39020         var icon = this.el.select('i.fa-star', true).first();
39021
39022         if(label && !icon){
39023             this.el.select('.roo-date-split-field-label', true).createChild({
39024                 tag : 'i',
39025                 cls : 'text-danger fa fa-lg fa-star',
39026                 tooltip : 'This field is required',
39027                 style : 'margin-right:5px;'
39028             }, label, true);
39029         }
39030         
39031         this.fireEvent('invalid', this, msg);
39032     },
39033     
39034     clearInvalid : function()
39035     {
39036         var label = this.el.select('label', true).first();
39037         var icon = this.el.select('i.fa-star', true).first();
39038
39039         if(label && icon){
39040             icon.remove();
39041         }
39042         
39043         this.fireEvent('valid', this);
39044     },
39045     
39046     getName: function()
39047     {
39048         return this.name;
39049     }
39050     
39051 });
39052
39053  
39054
39055 /**
39056  * @class Roo.bootstrap.LayoutMasonry
39057  * @extends Roo.bootstrap.Component
39058  * @children Roo.bootstrap.Element Roo.bootstrap.Img Roo.bootstrap.MasonryBrick
39059  * Bootstrap Layout Masonry class
39060  *
39061  * This is based on 
39062  * http://masonry.desandro.com
39063  *
39064  * The idea is to render all the bricks based on vertical width...
39065  *
39066  * The original code extends 'outlayer' - we might need to use that....
39067
39068  * @constructor
39069  * Create a new Element
39070  * @param {Object} config The config object
39071  */
39072
39073 Roo.bootstrap.LayoutMasonry = function(config){
39074     
39075     Roo.bootstrap.LayoutMasonry.superclass.constructor.call(this, config);
39076     
39077     this.bricks = [];
39078     
39079     Roo.bootstrap.LayoutMasonry.register(this);
39080     
39081     this.addEvents({
39082         // raw events
39083         /**
39084          * @event layout
39085          * Fire after layout the items
39086          * @param {Roo.bootstrap.LayoutMasonry} this
39087          * @param {Roo.EventObject} e
39088          */
39089         "layout" : true
39090     });
39091     
39092 };
39093
39094 Roo.extend(Roo.bootstrap.LayoutMasonry, Roo.bootstrap.Component,  {
39095     
39096     /**
39097      * @cfg {Boolean} isLayoutInstant = no animation?
39098      */   
39099     isLayoutInstant : false, // needed?
39100    
39101     /**
39102      * @cfg {Number} boxWidth  width of the columns
39103      */   
39104     boxWidth : 450,
39105     
39106       /**
39107      * @cfg {Number} boxHeight  - 0 for square, or fix it at a certian height
39108      */   
39109     boxHeight : 0,
39110     
39111     /**
39112      * @cfg {Number} padWidth padding below box..
39113      */   
39114     padWidth : 10, 
39115     
39116     /**
39117      * @cfg {Number} gutter gutter width..
39118      */   
39119     gutter : 10,
39120     
39121      /**
39122      * @cfg {Number} maxCols maximum number of columns
39123      */   
39124     
39125     maxCols: 0,
39126     
39127     /**
39128      * @cfg {Boolean} isAutoInitial defalut true
39129      */   
39130     isAutoInitial : true, 
39131     
39132     containerWidth: 0,
39133     
39134     /**
39135      * @cfg {Boolean} isHorizontal defalut false
39136      */   
39137     isHorizontal : false, 
39138
39139     currentSize : null,
39140     
39141     tag: 'div',
39142     
39143     cls: '',
39144     
39145     bricks: null, //CompositeElement
39146     
39147     cols : 1,
39148     
39149     _isLayoutInited : false,
39150     
39151 //    isAlternative : false, // only use for vertical layout...
39152     
39153     /**
39154      * @cfg {Number} alternativePadWidth padding below box..
39155      */   
39156     alternativePadWidth : 50,
39157     
39158     selectedBrick : [],
39159     
39160     getAutoCreate : function(){
39161         
39162         var cfg = Roo.apply({}, Roo.bootstrap.LayoutMasonry.superclass.getAutoCreate.call(this));
39163         
39164         var cfg = {
39165             tag: this.tag,
39166             cls: 'blog-masonary-wrapper ' + this.cls,
39167             cn : {
39168                 cls : 'mas-boxes masonary'
39169             }
39170         };
39171         
39172         return cfg;
39173     },
39174     
39175     getChildContainer: function( )
39176     {
39177         if (this.boxesEl) {
39178             return this.boxesEl;
39179         }
39180         
39181         this.boxesEl = this.el.select('.mas-boxes').first();
39182         
39183         return this.boxesEl;
39184     },
39185     
39186     
39187     initEvents : function()
39188     {
39189         var _this = this;
39190         
39191         if(this.isAutoInitial){
39192             Roo.log('hook children rendered');
39193             this.on('childrenrendered', function() {
39194                 Roo.log('children rendered');
39195                 _this.initial();
39196             } ,this);
39197         }
39198     },
39199     
39200     initial : function()
39201     {
39202         this.selectedBrick = [];
39203         
39204         this.currentSize = this.el.getBox(true);
39205         
39206         Roo.EventManager.onWindowResize(this.resize, this); 
39207
39208         if(!this.isAutoInitial){
39209             this.layout();
39210             return;
39211         }
39212         
39213         this.layout();
39214         
39215         return;
39216         //this.layout.defer(500,this);
39217         
39218     },
39219     
39220     resize : function()
39221     {
39222         var cs = this.el.getBox(true);
39223         
39224         if (
39225                 this.currentSize.width == cs.width && 
39226                 this.currentSize.x == cs.x && 
39227                 this.currentSize.height == cs.height && 
39228                 this.currentSize.y == cs.y 
39229         ) {
39230             Roo.log("no change in with or X or Y");
39231             return;
39232         }
39233         
39234         this.currentSize = cs;
39235         
39236         this.layout();
39237         
39238     },
39239     
39240     layout : function()
39241     {   
39242         this._resetLayout();
39243         
39244         var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
39245         
39246         this.layoutItems( isInstant );
39247       
39248         this._isLayoutInited = true;
39249         
39250         this.fireEvent('layout', this);
39251         
39252     },
39253     
39254     _resetLayout : function()
39255     {
39256         if(this.isHorizontal){
39257             this.horizontalMeasureColumns();
39258             return;
39259         }
39260         
39261         this.verticalMeasureColumns();
39262         
39263     },
39264     
39265     verticalMeasureColumns : function()
39266     {
39267         this.getContainerWidth();
39268         
39269 //        if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
39270 //            this.colWidth = Math.floor(this.containerWidth * 0.8);
39271 //            return;
39272 //        }
39273         
39274         var boxWidth = this.boxWidth + this.padWidth;
39275         
39276         if(this.containerWidth < this.boxWidth){
39277             boxWidth = this.containerWidth
39278         }
39279         
39280         var containerWidth = this.containerWidth;
39281         
39282         var cols = Math.floor(containerWidth / boxWidth);
39283         
39284         this.cols = Math.max( cols, 1 );
39285         
39286         this.cols = this.maxCols > 0 ? Math.min( this.cols, this.maxCols ) : this.cols;
39287         
39288         var totalBoxWidth = this.cols * boxWidth - this.padWidth;
39289         
39290         var avail = Math.floor((containerWidth - totalBoxWidth) / this.cols);
39291         
39292         this.colWidth = boxWidth + avail - this.padWidth;
39293         
39294         this.unitWidth = Math.round((this.colWidth - (this.gutter * 2)) / 3);
39295         this.unitHeight = this.boxHeight > 0 ? this.boxHeight  : this.unitWidth;
39296     },
39297     
39298     horizontalMeasureColumns : function()
39299     {
39300         this.getContainerWidth();
39301         
39302         var boxWidth = this.boxWidth;
39303         
39304         if(this.containerWidth < boxWidth){
39305             boxWidth = this.containerWidth;
39306         }
39307         
39308         this.unitWidth = Math.floor((boxWidth - (this.gutter * 2)) / 3);
39309         
39310         this.el.setHeight(boxWidth);
39311         
39312     },
39313     
39314     getContainerWidth : function()
39315     {
39316         this.containerWidth = this.el.getBox(true).width;  //maybe use getComputedWidth
39317     },
39318     
39319     layoutItems : function( isInstant )
39320     {
39321         Roo.log(this.bricks);
39322         
39323         var items = Roo.apply([], this.bricks);
39324         
39325         if(this.isHorizontal){
39326             this._horizontalLayoutItems( items , isInstant );
39327             return;
39328         }
39329         
39330 //        if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
39331 //            this._verticalAlternativeLayoutItems( items , isInstant );
39332 //            return;
39333 //        }
39334         
39335         this._verticalLayoutItems( items , isInstant );
39336         
39337     },
39338     
39339     _verticalLayoutItems : function ( items , isInstant)
39340     {
39341         if ( !items || !items.length ) {
39342             return;
39343         }
39344         
39345         var standard = [
39346             ['xs', 'xs', 'xs', 'tall'],
39347             ['xs', 'xs', 'tall'],
39348             ['xs', 'xs', 'sm'],
39349             ['xs', 'xs', 'xs'],
39350             ['xs', 'tall'],
39351             ['xs', 'sm'],
39352             ['xs', 'xs'],
39353             ['xs'],
39354             
39355             ['sm', 'xs', 'xs'],
39356             ['sm', 'xs'],
39357             ['sm'],
39358             
39359             ['tall', 'xs', 'xs', 'xs'],
39360             ['tall', 'xs', 'xs'],
39361             ['tall', 'xs'],
39362             ['tall']
39363             
39364         ];
39365         
39366         var queue = [];
39367         
39368         var boxes = [];
39369         
39370         var box = [];
39371         
39372         Roo.each(items, function(item, k){
39373             
39374             switch (item.size) {
39375                 // these layouts take up a full box,
39376                 case 'md' :
39377                 case 'md-left' :
39378                 case 'md-right' :
39379                 case 'wide' :
39380                     
39381                     if(box.length){
39382                         boxes.push(box);
39383                         box = [];
39384                     }
39385                     
39386                     boxes.push([item]);
39387                     
39388                     break;
39389                     
39390                 case 'xs' :
39391                 case 'sm' :
39392                 case 'tall' :
39393                     
39394                     box.push(item);
39395                     
39396                     break;
39397                 default :
39398                     break;
39399                     
39400             }
39401             
39402         }, this);
39403         
39404         if(box.length){
39405             boxes.push(box);
39406             box = [];
39407         }
39408         
39409         var filterPattern = function(box, length)
39410         {
39411             if(!box.length){
39412                 return;
39413             }
39414             
39415             var match = false;
39416             
39417             var pattern = box.slice(0, length);
39418             
39419             var format = [];
39420             
39421             Roo.each(pattern, function(i){
39422                 format.push(i.size);
39423             }, this);
39424             
39425             Roo.each(standard, function(s){
39426                 
39427                 if(String(s) != String(format)){
39428                     return;
39429                 }
39430                 
39431                 match = true;
39432                 return false;
39433                 
39434             }, this);
39435             
39436             if(!match && length == 1){
39437                 return;
39438             }
39439             
39440             if(!match){
39441                 filterPattern(box, length - 1);
39442                 return;
39443             }
39444                 
39445             queue.push(pattern);
39446
39447             box = box.slice(length, box.length);
39448
39449             filterPattern(box, 4);
39450
39451             return;
39452             
39453         }
39454         
39455         Roo.each(boxes, function(box, k){
39456             
39457             if(!box.length){
39458                 return;
39459             }
39460             
39461             if(box.length == 1){
39462                 queue.push(box);
39463                 return;
39464             }
39465             
39466             filterPattern(box, 4);
39467             
39468         }, this);
39469         
39470         this._processVerticalLayoutQueue( queue, isInstant );
39471         
39472     },
39473     
39474 //    _verticalAlternativeLayoutItems : function( items , isInstant )
39475 //    {
39476 //        if ( !items || !items.length ) {
39477 //            return;
39478 //        }
39479 //
39480 //        this._processVerticalAlternativeLayoutQueue( items, isInstant );
39481 //        
39482 //    },
39483     
39484     _horizontalLayoutItems : function ( items , isInstant)
39485     {
39486         if ( !items || !items.length || items.length < 3) {
39487             return;
39488         }
39489         
39490         items.reverse();
39491         
39492         var eItems = items.slice(0, 3);
39493         
39494         items = items.slice(3, items.length);
39495         
39496         var standard = [
39497             ['xs', 'xs', 'xs', 'wide'],
39498             ['xs', 'xs', 'wide'],
39499             ['xs', 'xs', 'sm'],
39500             ['xs', 'xs', 'xs'],
39501             ['xs', 'wide'],
39502             ['xs', 'sm'],
39503             ['xs', 'xs'],
39504             ['xs'],
39505             
39506             ['sm', 'xs', 'xs'],
39507             ['sm', 'xs'],
39508             ['sm'],
39509             
39510             ['wide', 'xs', 'xs', 'xs'],
39511             ['wide', 'xs', 'xs'],
39512             ['wide', 'xs'],
39513             ['wide'],
39514             
39515             ['wide-thin']
39516         ];
39517         
39518         var queue = [];
39519         
39520         var boxes = [];
39521         
39522         var box = [];
39523         
39524         Roo.each(items, function(item, k){
39525             
39526             switch (item.size) {
39527                 case 'md' :
39528                 case 'md-left' :
39529                 case 'md-right' :
39530                 case 'tall' :
39531                     
39532                     if(box.length){
39533                         boxes.push(box);
39534                         box = [];
39535                     }
39536                     
39537                     boxes.push([item]);
39538                     
39539                     break;
39540                     
39541                 case 'xs' :
39542                 case 'sm' :
39543                 case 'wide' :
39544                 case 'wide-thin' :
39545                     
39546                     box.push(item);
39547                     
39548                     break;
39549                 default :
39550                     break;
39551                     
39552             }
39553             
39554         }, this);
39555         
39556         if(box.length){
39557             boxes.push(box);
39558             box = [];
39559         }
39560         
39561         var filterPattern = function(box, length)
39562         {
39563             if(!box.length){
39564                 return;
39565             }
39566             
39567             var match = false;
39568             
39569             var pattern = box.slice(0, length);
39570             
39571             var format = [];
39572             
39573             Roo.each(pattern, function(i){
39574                 format.push(i.size);
39575             }, this);
39576             
39577             Roo.each(standard, function(s){
39578                 
39579                 if(String(s) != String(format)){
39580                     return;
39581                 }
39582                 
39583                 match = true;
39584                 return false;
39585                 
39586             }, this);
39587             
39588             if(!match && length == 1){
39589                 return;
39590             }
39591             
39592             if(!match){
39593                 filterPattern(box, length - 1);
39594                 return;
39595             }
39596                 
39597             queue.push(pattern);
39598
39599             box = box.slice(length, box.length);
39600
39601             filterPattern(box, 4);
39602
39603             return;
39604             
39605         }
39606         
39607         Roo.each(boxes, function(box, k){
39608             
39609             if(!box.length){
39610                 return;
39611             }
39612             
39613             if(box.length == 1){
39614                 queue.push(box);
39615                 return;
39616             }
39617             
39618             filterPattern(box, 4);
39619             
39620         }, this);
39621         
39622         
39623         var prune = [];
39624         
39625         var pos = this.el.getBox(true);
39626         
39627         var minX = pos.x;
39628         
39629         var maxX = pos.right - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
39630         
39631         var hit_end = false;
39632         
39633         Roo.each(queue, function(box){
39634             
39635             if(hit_end){
39636                 
39637                 Roo.each(box, function(b){
39638                 
39639                     b.el.setVisibilityMode(Roo.Element.DISPLAY);
39640                     b.el.hide();
39641
39642                 }, this);
39643
39644                 return;
39645             }
39646             
39647             var mx = 0;
39648             
39649             Roo.each(box, function(b){
39650                 
39651                 b.el.setVisibilityMode(Roo.Element.DISPLAY);
39652                 b.el.show();
39653
39654                 mx = Math.max(mx, b.x);
39655                 
39656             }, this);
39657             
39658             maxX = maxX - this.unitWidth * mx - this.gutter * (mx - 1) - this.padWidth;
39659             
39660             if(maxX < minX){
39661                 
39662                 Roo.each(box, function(b){
39663                 
39664                     b.el.setVisibilityMode(Roo.Element.DISPLAY);
39665                     b.el.hide();
39666                     
39667                 }, this);
39668                 
39669                 hit_end = true;
39670                 
39671                 return;
39672             }
39673             
39674             prune.push(box);
39675             
39676         }, this);
39677         
39678         this._processHorizontalLayoutQueue( prune, eItems, isInstant );
39679     },
39680     
39681     /** Sets position of item in DOM
39682     * @param {Element} item
39683     * @param {Number} x - horizontal position
39684     * @param {Number} y - vertical position
39685     * @param {Boolean} isInstant - disables transitions
39686     */
39687     _processVerticalLayoutQueue : function( queue, isInstant )
39688     {
39689         var pos = this.el.getBox(true);
39690         var x = pos.x;
39691         var y = pos.y;
39692         var maxY = [];
39693         
39694         for (var i = 0; i < this.cols; i++){
39695             maxY[i] = pos.y;
39696         }
39697         
39698         Roo.each(queue, function(box, k){
39699             
39700             var col = k % this.cols;
39701             
39702             Roo.each(box, function(b,kk){
39703                 
39704                 b.el.position('absolute');
39705                 
39706                 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
39707                 var height = Math.floor(this.unitHeight * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
39708                 
39709                 if(b.size == 'md-left' || b.size == 'md-right'){
39710                     width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
39711                     height = Math.floor(this.unitHeight * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
39712                 }
39713                 
39714                 b.el.setWidth(width);
39715                 b.el.setHeight(height);
39716                 // iframe?
39717                 b.el.select('iframe',true).setSize(width,height);
39718                 
39719             }, this);
39720             
39721             for (var i = 0; i < this.cols; i++){
39722                 
39723                 if(maxY[i] < maxY[col]){
39724                     col = i;
39725                     continue;
39726                 }
39727                 
39728                 col = Math.min(col, i);
39729                 
39730             }
39731             
39732             x = pos.x + col * (this.colWidth + this.padWidth);
39733             
39734             y = maxY[col];
39735             
39736             var positions = [];
39737             
39738             switch (box.length){
39739                 case 1 :
39740                     positions = this.getVerticalOneBoxColPositions(x, y, box);
39741                     break;
39742                 case 2 :
39743                     positions = this.getVerticalTwoBoxColPositions(x, y, box);
39744                     break;
39745                 case 3 :
39746                     positions = this.getVerticalThreeBoxColPositions(x, y, box);
39747                     break;
39748                 case 4 :
39749                     positions = this.getVerticalFourBoxColPositions(x, y, box);
39750                     break;
39751                 default :
39752                     break;
39753             }
39754             
39755             Roo.each(box, function(b,kk){
39756                 
39757                 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
39758                 
39759                 var sz = b.el.getSize();
39760                 
39761                 maxY[col] = Math.max(maxY[col], positions[kk].y + sz.height + this.padWidth);
39762                 
39763             }, this);
39764             
39765         }, this);
39766         
39767         var mY = 0;
39768         
39769         for (var i = 0; i < this.cols; i++){
39770             mY = Math.max(mY, maxY[i]);
39771         }
39772         
39773         this.el.setHeight(mY - pos.y);
39774         
39775     },
39776     
39777 //    _processVerticalAlternativeLayoutQueue : function( items, isInstant )
39778 //    {
39779 //        var pos = this.el.getBox(true);
39780 //        var x = pos.x;
39781 //        var y = pos.y;
39782 //        var maxX = pos.right;
39783 //        
39784 //        var maxHeight = 0;
39785 //        
39786 //        Roo.each(items, function(item, k){
39787 //            
39788 //            var c = k % 2;
39789 //            
39790 //            item.el.position('absolute');
39791 //                
39792 //            var width = Math.floor(this.colWidth + item.el.getPadding('lr'));
39793 //
39794 //            item.el.setWidth(width);
39795 //
39796 //            var height = Math.floor(this.colWidth * item.y / item.x + item.el.getPadding('tb'));
39797 //
39798 //            item.el.setHeight(height);
39799 //            
39800 //            if(c == 0){
39801 //                item.el.setXY([x, y], isInstant ? false : true);
39802 //            } else {
39803 //                item.el.setXY([maxX - width, y], isInstant ? false : true);
39804 //            }
39805 //            
39806 //            y = y + height + this.alternativePadWidth;
39807 //            
39808 //            maxHeight = maxHeight + height + this.alternativePadWidth;
39809 //            
39810 //        }, this);
39811 //        
39812 //        this.el.setHeight(maxHeight);
39813 //        
39814 //    },
39815     
39816     _processHorizontalLayoutQueue : function( queue, eItems, isInstant )
39817     {
39818         var pos = this.el.getBox(true);
39819         
39820         var minX = pos.x;
39821         var minY = pos.y;
39822         
39823         var maxX = pos.right;
39824         
39825         this._processHorizontalEndItem(eItems, maxX, minX, minY, isInstant);
39826         
39827         var maxX = maxX - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
39828         
39829         Roo.each(queue, function(box, k){
39830             
39831             Roo.each(box, function(b, kk){
39832                 
39833                 b.el.position('absolute');
39834                 
39835                 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
39836                 var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
39837                 
39838                 if(b.size == 'md-left' || b.size == 'md-right'){
39839                     width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
39840                     height = Math.floor(this.unitWidth * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
39841                 }
39842                 
39843                 b.el.setWidth(width);
39844                 b.el.setHeight(height);
39845                 
39846             }, this);
39847             
39848             if(!box.length){
39849                 return;
39850             }
39851             
39852             var positions = [];
39853             
39854             switch (box.length){
39855                 case 1 :
39856                     positions = this.getHorizontalOneBoxColPositions(maxX, minY, box);
39857                     break;
39858                 case 2 :
39859                     positions = this.getHorizontalTwoBoxColPositions(maxX, minY, box);
39860                     break;
39861                 case 3 :
39862                     positions = this.getHorizontalThreeBoxColPositions(maxX, minY, box);
39863                     break;
39864                 case 4 :
39865                     positions = this.getHorizontalFourBoxColPositions(maxX, minY, box);
39866                     break;
39867                 default :
39868                     break;
39869             }
39870             
39871             Roo.each(box, function(b,kk){
39872                 
39873                 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
39874                 
39875                 maxX = Math.min(maxX, positions[kk].x - this.padWidth);
39876                 
39877             }, this);
39878             
39879         }, this);
39880         
39881     },
39882     
39883     _processHorizontalEndItem : function(eItems, maxX, minX, minY, isInstant)
39884     {
39885         Roo.each(eItems, function(b,k){
39886             
39887             b.size = (k == 0) ? 'sm' : 'xs';
39888             b.x = (k == 0) ? 2 : 1;
39889             b.y = (k == 0) ? 2 : 1;
39890             
39891             b.el.position('absolute');
39892             
39893             var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
39894                 
39895             b.el.setWidth(width);
39896             
39897             var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
39898             
39899             b.el.setHeight(height);
39900             
39901         }, this);
39902
39903         var positions = [];
39904         
39905         positions.push({
39906             x : maxX - this.unitWidth * 2 - this.gutter,
39907             y : minY
39908         });
39909         
39910         positions.push({
39911             x : maxX - this.unitWidth,
39912             y : minY + (this.unitWidth + this.gutter) * 2
39913         });
39914         
39915         positions.push({
39916             x : maxX - this.unitWidth * 3 - this.gutter * 2,
39917             y : minY
39918         });
39919         
39920         Roo.each(eItems, function(b,k){
39921             
39922             b.el.setXY([positions[k].x, positions[k].y], isInstant ? false : true);
39923
39924         }, this);
39925         
39926     },
39927     
39928     getVerticalOneBoxColPositions : function(x, y, box)
39929     {
39930         var pos = [];
39931         
39932         var rand = Math.floor(Math.random() * ((4 - box[0].x)));
39933         
39934         if(box[0].size == 'md-left'){
39935             rand = 0;
39936         }
39937         
39938         if(box[0].size == 'md-right'){
39939             rand = 1;
39940         }
39941         
39942         pos.push({
39943             x : x + (this.unitWidth + this.gutter) * rand,
39944             y : y
39945         });
39946         
39947         return pos;
39948     },
39949     
39950     getVerticalTwoBoxColPositions : function(x, y, box)
39951     {
39952         var pos = [];
39953         
39954         if(box[0].size == 'xs'){
39955             
39956             pos.push({
39957                 x : x,
39958                 y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[1].y))
39959             });
39960
39961             pos.push({
39962                 x : x + (this.unitWidth + this.gutter) * (3 - box[1].x),
39963                 y : y
39964             });
39965             
39966             return pos;
39967             
39968         }
39969         
39970         pos.push({
39971             x : x,
39972             y : y
39973         });
39974
39975         pos.push({
39976             x : x + (this.unitWidth + this.gutter) * 2,
39977             y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[0].y))
39978         });
39979         
39980         return pos;
39981         
39982     },
39983     
39984     getVerticalThreeBoxColPositions : function(x, y, box)
39985     {
39986         var pos = [];
39987         
39988         if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
39989             
39990             pos.push({
39991                 x : x,
39992                 y : y
39993             });
39994
39995             pos.push({
39996                 x : x + (this.unitWidth + this.gutter) * 1,
39997                 y : y
39998             });
39999             
40000             pos.push({
40001                 x : x + (this.unitWidth + this.gutter) * 2,
40002                 y : y
40003             });
40004             
40005             return pos;
40006             
40007         }
40008         
40009         if(box[0].size == 'xs' && box[1].size == 'xs'){
40010             
40011             pos.push({
40012                 x : x,
40013                 y : y
40014             });
40015
40016             pos.push({
40017                 x : x,
40018                 y : y + ((this.unitHeight + this.gutter) * (box[2].y - 1))
40019             });
40020             
40021             pos.push({
40022                 x : x + (this.unitWidth + this.gutter) * 1,
40023                 y : y
40024             });
40025             
40026             return pos;
40027             
40028         }
40029         
40030         pos.push({
40031             x : x,
40032             y : y
40033         });
40034
40035         pos.push({
40036             x : x + (this.unitWidth + this.gutter) * 2,
40037             y : y
40038         });
40039
40040         pos.push({
40041             x : x + (this.unitWidth + this.gutter) * 2,
40042             y : y + (this.unitHeight + this.gutter) * (box[0].y - 1)
40043         });
40044             
40045         return pos;
40046         
40047     },
40048     
40049     getVerticalFourBoxColPositions : function(x, y, box)
40050     {
40051         var pos = [];
40052         
40053         if(box[0].size == 'xs'){
40054             
40055             pos.push({
40056                 x : x,
40057                 y : y
40058             });
40059
40060             pos.push({
40061                 x : x,
40062                 y : y + (this.unitHeight + this.gutter) * 1
40063             });
40064             
40065             pos.push({
40066                 x : x,
40067                 y : y + (this.unitHeight + this.gutter) * 2
40068             });
40069             
40070             pos.push({
40071                 x : x + (this.unitWidth + this.gutter) * 1,
40072                 y : y
40073             });
40074             
40075             return pos;
40076             
40077         }
40078         
40079         pos.push({
40080             x : x,
40081             y : y
40082         });
40083
40084         pos.push({
40085             x : x + (this.unitWidth + this.gutter) * 2,
40086             y : y
40087         });
40088
40089         pos.push({
40090             x : x + (this.unitHeightunitWidth + this.gutter) * 2,
40091             y : y + (this.unitHeight + this.gutter) * 1
40092         });
40093
40094         pos.push({
40095             x : x + (this.unitWidth + this.gutter) * 2,
40096             y : y + (this.unitWidth + this.gutter) * 2
40097         });
40098
40099         return pos;
40100         
40101     },
40102     
40103     getHorizontalOneBoxColPositions : function(maxX, minY, box)
40104     {
40105         var pos = [];
40106         
40107         if(box[0].size == 'md-left'){
40108             pos.push({
40109                 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
40110                 y : minY
40111             });
40112             
40113             return pos;
40114         }
40115         
40116         if(box[0].size == 'md-right'){
40117             pos.push({
40118                 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
40119                 y : minY + (this.unitWidth + this.gutter) * 1
40120             });
40121             
40122             return pos;
40123         }
40124         
40125         var rand = Math.floor(Math.random() * (4 - box[0].y));
40126         
40127         pos.push({
40128             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
40129             y : minY + (this.unitWidth + this.gutter) * rand
40130         });
40131         
40132         return pos;
40133         
40134     },
40135     
40136     getHorizontalTwoBoxColPositions : function(maxX, minY, box)
40137     {
40138         var pos = [];
40139         
40140         if(box[0].size == 'xs'){
40141             
40142             pos.push({
40143                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
40144                 y : minY
40145             });
40146
40147             pos.push({
40148                 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
40149                 y : minY + (this.unitWidth + this.gutter) * (3 - box[1].y)
40150             });
40151             
40152             return pos;
40153             
40154         }
40155         
40156         pos.push({
40157             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
40158             y : minY
40159         });
40160
40161         pos.push({
40162             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
40163             y : minY + (this.unitWidth + this.gutter) * 2
40164         });
40165         
40166         return pos;
40167         
40168     },
40169     
40170     getHorizontalThreeBoxColPositions : function(maxX, minY, box)
40171     {
40172         var pos = [];
40173         
40174         if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
40175             
40176             pos.push({
40177                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
40178                 y : minY
40179             });
40180
40181             pos.push({
40182                 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
40183                 y : minY + (this.unitWidth + this.gutter) * 1
40184             });
40185             
40186             pos.push({
40187                 x : maxX - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
40188                 y : minY + (this.unitWidth + this.gutter) * 2
40189             });
40190             
40191             return pos;
40192             
40193         }
40194         
40195         if(box[0].size == 'xs' && box[1].size == 'xs'){
40196             
40197             pos.push({
40198                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
40199                 y : minY
40200             });
40201
40202             pos.push({
40203                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
40204                 y : minY
40205             });
40206             
40207             pos.push({
40208                 x : maxX - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
40209                 y : minY + (this.unitWidth + this.gutter) * 1
40210             });
40211             
40212             return pos;
40213             
40214         }
40215         
40216         pos.push({
40217             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
40218             y : minY
40219         });
40220
40221         pos.push({
40222             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
40223             y : minY + (this.unitWidth + this.gutter) * 2
40224         });
40225
40226         pos.push({
40227             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
40228             y : minY + (this.unitWidth + this.gutter) * 2
40229         });
40230             
40231         return pos;
40232         
40233     },
40234     
40235     getHorizontalFourBoxColPositions : function(maxX, minY, box)
40236     {
40237         var pos = [];
40238         
40239         if(box[0].size == 'xs'){
40240             
40241             pos.push({
40242                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
40243                 y : minY
40244             });
40245
40246             pos.push({
40247                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
40248                 y : minY
40249             });
40250             
40251             pos.push({
40252                 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),
40253                 y : minY
40254             });
40255             
40256             pos.push({
40257                 x : maxX - this.unitWidth * box[3].x - this.gutter * (box[3].x - 1),
40258                 y : minY + (this.unitWidth + this.gutter) * 1
40259             });
40260             
40261             return pos;
40262             
40263         }
40264         
40265         pos.push({
40266             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
40267             y : minY
40268         });
40269         
40270         pos.push({
40271             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
40272             y : minY + (this.unitWidth + this.gutter) * 2
40273         });
40274         
40275         pos.push({
40276             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
40277             y : minY + (this.unitWidth + this.gutter) * 2
40278         });
40279         
40280         pos.push({
40281             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),
40282             y : minY + (this.unitWidth + this.gutter) * 2
40283         });
40284
40285         return pos;
40286         
40287     },
40288     
40289     /**
40290     * remove a Masonry Brick
40291     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to remove
40292     */
40293     removeBrick : function(brick_id)
40294     {
40295         if (!brick_id) {
40296             return;
40297         }
40298         
40299         for (var i = 0; i<this.bricks.length; i++) {
40300             if (this.bricks[i].id == brick_id) {
40301                 this.bricks.splice(i,1);
40302                 this.el.dom.removeChild(Roo.get(brick_id).dom);
40303                 this.initial();
40304             }
40305         }
40306     },
40307     
40308     /**
40309     * adds a Masonry Brick
40310     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
40311     */
40312     addBrick : function(cfg)
40313     {
40314         var cn = new Roo.bootstrap.MasonryBrick(cfg);
40315         //this.register(cn);
40316         cn.parentId = this.id;
40317         cn.render(this.el);
40318         return cn;
40319     },
40320     
40321     /**
40322     * register a Masonry Brick
40323     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
40324     */
40325     
40326     register : function(brick)
40327     {
40328         this.bricks.push(brick);
40329         brick.masonryId = this.id;
40330     },
40331     
40332     /**
40333     * clear all the Masonry Brick
40334     */
40335     clearAll : function()
40336     {
40337         this.bricks = [];
40338         //this.getChildContainer().dom.innerHTML = "";
40339         this.el.dom.innerHTML = '';
40340     },
40341     
40342     getSelected : function()
40343     {
40344         if (!this.selectedBrick) {
40345             return false;
40346         }
40347         
40348         return this.selectedBrick;
40349     }
40350 });
40351
40352 Roo.apply(Roo.bootstrap.LayoutMasonry, {
40353     
40354     groups: {},
40355      /**
40356     * register a Masonry Layout
40357     * @param {Roo.bootstrap.LayoutMasonry} the masonry layout to add
40358     */
40359     
40360     register : function(layout)
40361     {
40362         this.groups[layout.id] = layout;
40363     },
40364     /**
40365     * fetch a  Masonry Layout based on the masonry layout ID
40366     * @param {string} the masonry layout to add
40367     * @returns {Roo.bootstrap.LayoutMasonry} the masonry layout
40368     */
40369     
40370     get: function(layout_id) {
40371         if (typeof(this.groups[layout_id]) == 'undefined') {
40372             return false;
40373         }
40374         return this.groups[layout_id] ;
40375     }
40376     
40377     
40378     
40379 });
40380
40381  
40382
40383  /**
40384  *
40385  * This is based on 
40386  * http://masonry.desandro.com
40387  *
40388  * The idea is to render all the bricks based on vertical width...
40389  *
40390  * The original code extends 'outlayer' - we might need to use that....
40391  * 
40392  */
40393
40394
40395 /**
40396  * @class Roo.bootstrap.LayoutMasonryAuto
40397  * @extends Roo.bootstrap.Component
40398  * Bootstrap Layout Masonry class
40399  * 
40400  * @constructor
40401  * Create a new Element
40402  * @param {Object} config The config object
40403  */
40404
40405 Roo.bootstrap.LayoutMasonryAuto = function(config){
40406     Roo.bootstrap.LayoutMasonryAuto.superclass.constructor.call(this, config);
40407 };
40408
40409 Roo.extend(Roo.bootstrap.LayoutMasonryAuto, Roo.bootstrap.Component,  {
40410     
40411       /**
40412      * @cfg {Boolean} isFitWidth  - resize the width..
40413      */   
40414     isFitWidth : false,  // options..
40415     /**
40416      * @cfg {Boolean} isOriginLeft = left align?
40417      */   
40418     isOriginLeft : true,
40419     /**
40420      * @cfg {Boolean} isOriginTop = top align?
40421      */   
40422     isOriginTop : false,
40423     /**
40424      * @cfg {Boolean} isLayoutInstant = no animation?
40425      */   
40426     isLayoutInstant : false, // needed?
40427     /**
40428      * @cfg {Boolean} isResizingContainer = not sure if this is used..
40429      */   
40430     isResizingContainer : true,
40431     /**
40432      * @cfg {Number} columnWidth  width of the columns 
40433      */   
40434     
40435     columnWidth : 0,
40436     
40437     /**
40438      * @cfg {Number} maxCols maximum number of columns
40439      */   
40440     
40441     maxCols: 0,
40442     /**
40443      * @cfg {Number} padHeight padding below box..
40444      */   
40445     
40446     padHeight : 10, 
40447     
40448     /**
40449      * @cfg {Boolean} isAutoInitial defalut true
40450      */   
40451     
40452     isAutoInitial : true, 
40453     
40454     // private?
40455     gutter : 0,
40456     
40457     containerWidth: 0,
40458     initialColumnWidth : 0,
40459     currentSize : null,
40460     
40461     colYs : null, // array.
40462     maxY : 0,
40463     padWidth: 10,
40464     
40465     
40466     tag: 'div',
40467     cls: '',
40468     bricks: null, //CompositeElement
40469     cols : 0, // array?
40470     // element : null, // wrapped now this.el
40471     _isLayoutInited : null, 
40472     
40473     
40474     getAutoCreate : function(){
40475         
40476         var cfg = {
40477             tag: this.tag,
40478             cls: 'blog-masonary-wrapper ' + this.cls,
40479             cn : {
40480                 cls : 'mas-boxes masonary'
40481             }
40482         };
40483         
40484         return cfg;
40485     },
40486     
40487     getChildContainer: function( )
40488     {
40489         if (this.boxesEl) {
40490             return this.boxesEl;
40491         }
40492         
40493         this.boxesEl = this.el.select('.mas-boxes').first();
40494         
40495         return this.boxesEl;
40496     },
40497     
40498     
40499     initEvents : function()
40500     {
40501         var _this = this;
40502         
40503         if(this.isAutoInitial){
40504             Roo.log('hook children rendered');
40505             this.on('childrenrendered', function() {
40506                 Roo.log('children rendered');
40507                 _this.initial();
40508             } ,this);
40509         }
40510         
40511     },
40512     
40513     initial : function()
40514     {
40515         this.reloadItems();
40516
40517         this.currentSize = this.el.getBox(true);
40518
40519         /// was window resize... - let's see if this works..
40520         Roo.EventManager.onWindowResize(this.resize, this); 
40521
40522         if(!this.isAutoInitial){
40523             this.layout();
40524             return;
40525         }
40526         
40527         this.layout.defer(500,this);
40528     },
40529     
40530     reloadItems: function()
40531     {
40532         this.bricks = this.el.select('.masonry-brick', true);
40533         
40534         this.bricks.each(function(b) {
40535             //Roo.log(b.getSize());
40536             if (!b.attr('originalwidth')) {
40537                 b.attr('originalwidth',  b.getSize().width);
40538             }
40539             
40540         });
40541         
40542         Roo.log(this.bricks.elements.length);
40543     },
40544     
40545     resize : function()
40546     {
40547         Roo.log('resize');
40548         var cs = this.el.getBox(true);
40549         
40550         if (this.currentSize.width == cs.width && this.currentSize.x == cs.x ) {
40551             Roo.log("no change in with or X");
40552             return;
40553         }
40554         this.currentSize = cs;
40555         this.layout();
40556     },
40557     
40558     layout : function()
40559     {
40560          Roo.log('layout');
40561         this._resetLayout();
40562         //this._manageStamps();
40563       
40564         // don't animate first layout
40565         var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
40566         this.layoutItems( isInstant );
40567       
40568         // flag for initalized
40569         this._isLayoutInited = true;
40570     },
40571     
40572     layoutItems : function( isInstant )
40573     {
40574         //var items = this._getItemsForLayout( this.items );
40575         // original code supports filtering layout items.. we just ignore it..
40576         
40577         this._layoutItems( this.bricks , isInstant );
40578       
40579         this._postLayout();
40580     },
40581     _layoutItems : function ( items , isInstant)
40582     {
40583        //this.fireEvent( 'layout', this, items );
40584     
40585
40586         if ( !items || !items.elements.length ) {
40587           // no items, emit event with empty array
40588             return;
40589         }
40590
40591         var queue = [];
40592         items.each(function(item) {
40593             Roo.log("layout item");
40594             Roo.log(item);
40595             // get x/y object from method
40596             var position = this._getItemLayoutPosition( item );
40597             // enqueue
40598             position.item = item;
40599             position.isInstant = isInstant; // || item.isLayoutInstant; << not set yet...
40600             queue.push( position );
40601         }, this);
40602       
40603         this._processLayoutQueue( queue );
40604     },
40605     /** Sets position of item in DOM
40606     * @param {Element} item
40607     * @param {Number} x - horizontal position
40608     * @param {Number} y - vertical position
40609     * @param {Boolean} isInstant - disables transitions
40610     */
40611     _processLayoutQueue : function( queue )
40612     {
40613         for ( var i=0, len = queue.length; i < len; i++ ) {
40614             var obj = queue[i];
40615             obj.item.position('absolute');
40616             obj.item.setXY([obj.x,obj.y], obj.isInstant ? false : true);
40617         }
40618     },
40619       
40620     
40621     /**
40622     * Any logic you want to do after each layout,
40623     * i.e. size the container
40624     */
40625     _postLayout : function()
40626     {
40627         this.resizeContainer();
40628     },
40629     
40630     resizeContainer : function()
40631     {
40632         if ( !this.isResizingContainer ) {
40633             return;
40634         }
40635         var size = this._getContainerSize();
40636         if ( size ) {
40637             this.el.setSize(size.width,size.height);
40638             this.boxesEl.setSize(size.width,size.height);
40639         }
40640     },
40641     
40642     
40643     
40644     _resetLayout : function()
40645     {
40646         //this.getSize();  // -- does not really do anything.. it probably applies left/right etc. to obuject but not used
40647         this.colWidth = this.el.getWidth();
40648         //this.gutter = this.el.getWidth(); 
40649         
40650         this.measureColumns();
40651
40652         // reset column Y
40653         var i = this.cols;
40654         this.colYs = [];
40655         while (i--) {
40656             this.colYs.push( 0 );
40657         }
40658     
40659         this.maxY = 0;
40660     },
40661
40662     measureColumns : function()
40663     {
40664         this.getContainerWidth();
40665       // if columnWidth is 0, default to outerWidth of first item
40666         if ( !this.columnWidth ) {
40667             var firstItem = this.bricks.first();
40668             Roo.log(firstItem);
40669             this.columnWidth  = this.containerWidth;
40670             if (firstItem && firstItem.attr('originalwidth') ) {
40671                 this.columnWidth = 1* (firstItem.attr('originalwidth') || firstItem.getWidth());
40672             }
40673             // columnWidth fall back to item of first element
40674             Roo.log("set column width?");
40675                         this.initialColumnWidth = this.columnWidth  ;
40676
40677             // if first elem has no width, default to size of container
40678             
40679         }
40680         
40681         
40682         if (this.initialColumnWidth) {
40683             this.columnWidth = this.initialColumnWidth;
40684         }
40685         
40686         
40687             
40688         // column width is fixed at the top - however if container width get's smaller we should
40689         // reduce it...
40690         
40691         // this bit calcs how man columns..
40692             
40693         var columnWidth = this.columnWidth += this.gutter;
40694       
40695         // calculate columns
40696         var containerWidth = this.containerWidth + this.gutter;
40697         
40698         var cols = (containerWidth - this.padWidth) / (columnWidth - this.padWidth);
40699         // fix rounding errors, typically with gutters
40700         var excess = columnWidth - containerWidth % columnWidth;
40701         
40702         
40703         // if overshoot is less than a pixel, round up, otherwise floor it
40704         var mathMethod = excess && excess < 1 ? 'round' : 'floor';
40705         cols = Math[ mathMethod ]( cols );
40706         this.cols = Math.max( cols, 1 );
40707         this.cols = this.maxCols > 0 ? Math.min( this.cols, this.maxCols ) : this.cols;
40708         
40709          // padding positioning..
40710         var totalColWidth = this.cols * this.columnWidth;
40711         var padavail = this.containerWidth - totalColWidth;
40712         // so for 2 columns - we need 3 'pads'
40713         
40714         var padNeeded = (1+this.cols) * this.padWidth;
40715         
40716         var padExtra = Math.floor((padavail - padNeeded) / this.cols);
40717         
40718         this.columnWidth += padExtra
40719         //this.padWidth = Math.floor(padavail /  ( this.cols));
40720         
40721         // adjust colum width so that padding is fixed??
40722         
40723         // we have 3 columns ... total = width * 3
40724         // we have X left over... that should be used by 
40725         
40726         //if (this.expandC) {
40727             
40728         //}
40729         
40730         
40731         
40732     },
40733     
40734     getContainerWidth : function()
40735     {
40736        /* // container is parent if fit width
40737         var container = this.isFitWidth ? this.element.parentNode : this.element;
40738         // check that this.size and size are there
40739         // IE8 triggers resize on body size change, so they might not be
40740         
40741         var size = getSize( container );  //FIXME
40742         this.containerWidth = size && size.innerWidth; //FIXME
40743         */
40744          
40745         this.containerWidth = this.el.getBox(true).width;  //maybe use getComputedWidth
40746         
40747     },
40748     
40749     _getItemLayoutPosition : function( item )  // what is item?
40750     {
40751         // we resize the item to our columnWidth..
40752       
40753         item.setWidth(this.columnWidth);
40754         item.autoBoxAdjust  = false;
40755         
40756         var sz = item.getSize();
40757  
40758         // how many columns does this brick span
40759         var remainder = this.containerWidth % this.columnWidth;
40760         
40761         var mathMethod = remainder && remainder < 1 ? 'round' : 'ceil';
40762         // round if off by 1 pixel, otherwise use ceil
40763         var colSpan = Math[ mathMethod ]( sz.width  / this.columnWidth );
40764         colSpan = Math.min( colSpan, this.cols );
40765         
40766         // normally this should be '1' as we dont' currently allow multi width columns..
40767         
40768         var colGroup = this._getColGroup( colSpan );
40769         // get the minimum Y value from the columns
40770         var minimumY = Math.min.apply( Math, colGroup );
40771         Roo.log([ 'setHeight',  minimumY, sz.height, setHeight ]);
40772         
40773         var shortColIndex = colGroup.indexOf(  minimumY ); // broken on ie8..?? probably...
40774          
40775         // position the brick
40776         var position = {
40777             x: this.currentSize.x + (this.padWidth /2) + ((this.columnWidth + this.padWidth )* shortColIndex),
40778             y: this.currentSize.y + minimumY + this.padHeight
40779         };
40780         
40781         Roo.log(position);
40782         // apply setHeight to necessary columns
40783         var setHeight = minimumY + sz.height + this.padHeight;
40784         //Roo.log([ 'setHeight',  minimumY, sz.height, setHeight ]);
40785         
40786         var setSpan = this.cols + 1 - colGroup.length;
40787         for ( var i = 0; i < setSpan; i++ ) {
40788           this.colYs[ shortColIndex + i ] = setHeight ;
40789         }
40790       
40791         return position;
40792     },
40793     
40794     /**
40795      * @param {Number} colSpan - number of columns the element spans
40796      * @returns {Array} colGroup
40797      */
40798     _getColGroup : function( colSpan )
40799     {
40800         if ( colSpan < 2 ) {
40801           // if brick spans only one column, use all the column Ys
40802           return this.colYs;
40803         }
40804       
40805         var colGroup = [];
40806         // how many different places could this brick fit horizontally
40807         var groupCount = this.cols + 1 - colSpan;
40808         // for each group potential horizontal position
40809         for ( var i = 0; i < groupCount; i++ ) {
40810           // make an array of colY values for that one group
40811           var groupColYs = this.colYs.slice( i, i + colSpan );
40812           // and get the max value of the array
40813           colGroup[i] = Math.max.apply( Math, groupColYs );
40814         }
40815         return colGroup;
40816     },
40817     /*
40818     _manageStamp : function( stamp )
40819     {
40820         var stampSize =  stamp.getSize();
40821         var offset = stamp.getBox();
40822         // get the columns that this stamp affects
40823         var firstX = this.isOriginLeft ? offset.x : offset.right;
40824         var lastX = firstX + stampSize.width;
40825         var firstCol = Math.floor( firstX / this.columnWidth );
40826         firstCol = Math.max( 0, firstCol );
40827         
40828         var lastCol = Math.floor( lastX / this.columnWidth );
40829         // lastCol should not go over if multiple of columnWidth #425
40830         lastCol -= lastX % this.columnWidth ? 0 : 1;
40831         lastCol = Math.min( this.cols - 1, lastCol );
40832         
40833         // set colYs to bottom of the stamp
40834         var stampMaxY = ( this.isOriginTop ? offset.y : offset.bottom ) +
40835             stampSize.height;
40836             
40837         for ( var i = firstCol; i <= lastCol; i++ ) {
40838           this.colYs[i] = Math.max( stampMaxY, this.colYs[i] );
40839         }
40840     },
40841     */
40842     
40843     _getContainerSize : function()
40844     {
40845         this.maxY = Math.max.apply( Math, this.colYs );
40846         var size = {
40847             height: this.maxY
40848         };
40849       
40850         if ( this.isFitWidth ) {
40851             size.width = this._getContainerFitWidth();
40852         }
40853       
40854         return size;
40855     },
40856     
40857     _getContainerFitWidth : function()
40858     {
40859         var unusedCols = 0;
40860         // count unused columns
40861         var i = this.cols;
40862         while ( --i ) {
40863           if ( this.colYs[i] !== 0 ) {
40864             break;
40865           }
40866           unusedCols++;
40867         }
40868         // fit container to columns that have been used
40869         return ( this.cols - unusedCols ) * this.columnWidth - this.gutter;
40870     },
40871     
40872     needsResizeLayout : function()
40873     {
40874         var previousWidth = this.containerWidth;
40875         this.getContainerWidth();
40876         return previousWidth !== this.containerWidth;
40877     }
40878  
40879 });
40880
40881  
40882
40883  /*
40884  * - LGPL
40885  *
40886  * element
40887  * 
40888  */
40889
40890 /**
40891  * @class Roo.bootstrap.MasonryBrick
40892  * @extends Roo.bootstrap.Component
40893  * Bootstrap MasonryBrick class
40894  * 
40895  * @constructor
40896  * Create a new MasonryBrick
40897  * @param {Object} config The config object
40898  */
40899
40900 Roo.bootstrap.MasonryBrick = function(config){
40901     
40902     Roo.bootstrap.MasonryBrick.superclass.constructor.call(this, config);
40903     
40904     Roo.bootstrap.MasonryBrick.register(this);
40905     
40906     this.addEvents({
40907         // raw events
40908         /**
40909          * @event click
40910          * When a MasonryBrick is clcik
40911          * @param {Roo.bootstrap.MasonryBrick} this
40912          * @param {Roo.EventObject} e
40913          */
40914         "click" : true
40915     });
40916 };
40917
40918 Roo.extend(Roo.bootstrap.MasonryBrick, Roo.bootstrap.Component,  {
40919     
40920     /**
40921      * @cfg {String} title
40922      */   
40923     title : '',
40924     /**
40925      * @cfg {String} html
40926      */   
40927     html : '',
40928     /**
40929      * @cfg {String} bgimage
40930      */   
40931     bgimage : '',
40932     /**
40933      * @cfg {String} videourl
40934      */   
40935     videourl : '',
40936     /**
40937      * @cfg {String} cls
40938      */   
40939     cls : '',
40940     /**
40941      * @cfg {String} href
40942      */   
40943     href : '',
40944     /**
40945      * @cfg {String} size (xs|sm|md|md-left|md-right|tall|wide)
40946      */   
40947     size : 'xs',
40948     
40949     /**
40950      * @cfg {String} placetitle (center|bottom)
40951      */   
40952     placetitle : '',
40953     
40954     /**
40955      * @cfg {Boolean} isFitContainer defalut true
40956      */   
40957     isFitContainer : true, 
40958     
40959     /**
40960      * @cfg {Boolean} preventDefault defalut false
40961      */   
40962     preventDefault : false, 
40963     
40964     /**
40965      * @cfg {Boolean} inverse defalut false
40966      */   
40967     maskInverse : false, 
40968     
40969     getAutoCreate : function()
40970     {
40971         if(!this.isFitContainer){
40972             return this.getSplitAutoCreate();
40973         }
40974         
40975         var cls = 'masonry-brick masonry-brick-full';
40976         
40977         if(this.href.length){
40978             cls += ' masonry-brick-link';
40979         }
40980         
40981         if(this.bgimage.length){
40982             cls += ' masonry-brick-image';
40983         }
40984         
40985         if(this.maskInverse){
40986             cls += ' mask-inverse';
40987         }
40988         
40989         if(!this.html.length && !this.maskInverse && !this.videourl.length){
40990             cls += ' enable-mask';
40991         }
40992         
40993         if(this.size){
40994             cls += ' masonry-' + this.size + '-brick';
40995         }
40996         
40997         if(this.placetitle.length){
40998             
40999             switch (this.placetitle) {
41000                 case 'center' :
41001                     cls += ' masonry-center-title';
41002                     break;
41003                 case 'bottom' :
41004                     cls += ' masonry-bottom-title';
41005                     break;
41006                 default:
41007                     break;
41008             }
41009             
41010         } else {
41011             if(!this.html.length && !this.bgimage.length){
41012                 cls += ' masonry-center-title';
41013             }
41014
41015             if(!this.html.length && this.bgimage.length){
41016                 cls += ' masonry-bottom-title';
41017             }
41018         }
41019         
41020         if(this.cls){
41021             cls += ' ' + this.cls;
41022         }
41023         
41024         var cfg = {
41025             tag: (this.href.length) ? 'a' : 'div',
41026             cls: cls,
41027             cn: [
41028                 {
41029                     tag: 'div',
41030                     cls: 'masonry-brick-mask'
41031                 },
41032                 {
41033                     tag: 'div',
41034                     cls: 'masonry-brick-paragraph',
41035                     cn: []
41036                 }
41037             ]
41038         };
41039         
41040         if(this.href.length){
41041             cfg.href = this.href;
41042         }
41043         
41044         var cn = cfg.cn[1].cn;
41045         
41046         if(this.title.length){
41047             cn.push({
41048                 tag: 'h4',
41049                 cls: 'masonry-brick-title',
41050                 html: this.title
41051             });
41052         }
41053         
41054         if(this.html.length){
41055             cn.push({
41056                 tag: 'p',
41057                 cls: 'masonry-brick-text',
41058                 html: this.html
41059             });
41060         }
41061         
41062         if (!this.title.length && !this.html.length) {
41063             cfg.cn[1].cls += ' hide';
41064         }
41065         
41066         if(this.bgimage.length){
41067             cfg.cn.push({
41068                 tag: 'img',
41069                 cls: 'masonry-brick-image-view',
41070                 src: this.bgimage
41071             });
41072         }
41073         
41074         if(this.videourl.length){
41075             var vurl = this.videourl.replace(/https:\/\/youtu\.be/, 'https://www.youtube.com/embed/');
41076             // youtube support only?
41077             cfg.cn.push({
41078                 tag: 'iframe',
41079                 cls: 'masonry-brick-image-view',
41080                 src: vurl,
41081                 frameborder : 0,
41082                 allowfullscreen : true
41083             });
41084         }
41085         
41086         return cfg;
41087         
41088     },
41089     
41090     getSplitAutoCreate : function()
41091     {
41092         var cls = 'masonry-brick masonry-brick-split';
41093         
41094         if(this.href.length){
41095             cls += ' masonry-brick-link';
41096         }
41097         
41098         if(this.bgimage.length){
41099             cls += ' masonry-brick-image';
41100         }
41101         
41102         if(this.size){
41103             cls += ' masonry-' + this.size + '-brick';
41104         }
41105         
41106         switch (this.placetitle) {
41107             case 'center' :
41108                 cls += ' masonry-center-title';
41109                 break;
41110             case 'bottom' :
41111                 cls += ' masonry-bottom-title';
41112                 break;
41113             default:
41114                 if(!this.bgimage.length){
41115                     cls += ' masonry-center-title';
41116                 }
41117
41118                 if(this.bgimage.length){
41119                     cls += ' masonry-bottom-title';
41120                 }
41121                 break;
41122         }
41123         
41124         if(this.cls){
41125             cls += ' ' + this.cls;
41126         }
41127         
41128         var cfg = {
41129             tag: (this.href.length) ? 'a' : 'div',
41130             cls: cls,
41131             cn: [
41132                 {
41133                     tag: 'div',
41134                     cls: 'masonry-brick-split-head',
41135                     cn: [
41136                         {
41137                             tag: 'div',
41138                             cls: 'masonry-brick-paragraph',
41139                             cn: []
41140                         }
41141                     ]
41142                 },
41143                 {
41144                     tag: 'div',
41145                     cls: 'masonry-brick-split-body',
41146                     cn: []
41147                 }
41148             ]
41149         };
41150         
41151         if(this.href.length){
41152             cfg.href = this.href;
41153         }
41154         
41155         if(this.title.length){
41156             cfg.cn[0].cn[0].cn.push({
41157                 tag: 'h4',
41158                 cls: 'masonry-brick-title',
41159                 html: this.title
41160             });
41161         }
41162         
41163         if(this.html.length){
41164             cfg.cn[1].cn.push({
41165                 tag: 'p',
41166                 cls: 'masonry-brick-text',
41167                 html: this.html
41168             });
41169         }
41170
41171         if(this.bgimage.length){
41172             cfg.cn[0].cn.push({
41173                 tag: 'img',
41174                 cls: 'masonry-brick-image-view',
41175                 src: this.bgimage
41176             });
41177         }
41178         
41179         if(this.videourl.length){
41180             var vurl = this.videourl.replace(/https:\/\/youtu\.be/, 'https://www.youtube.com/embed/');
41181             // youtube support only?
41182             cfg.cn[0].cn.cn.push({
41183                 tag: 'iframe',
41184                 cls: 'masonry-brick-image-view',
41185                 src: vurl,
41186                 frameborder : 0,
41187                 allowfullscreen : true
41188             });
41189         }
41190         
41191         return cfg;
41192     },
41193     
41194     initEvents: function() 
41195     {
41196         switch (this.size) {
41197             case 'xs' :
41198                 this.x = 1;
41199                 this.y = 1;
41200                 break;
41201             case 'sm' :
41202                 this.x = 2;
41203                 this.y = 2;
41204                 break;
41205             case 'md' :
41206             case 'md-left' :
41207             case 'md-right' :
41208                 this.x = 3;
41209                 this.y = 3;
41210                 break;
41211             case 'tall' :
41212                 this.x = 2;
41213                 this.y = 3;
41214                 break;
41215             case 'wide' :
41216                 this.x = 3;
41217                 this.y = 2;
41218                 break;
41219             case 'wide-thin' :
41220                 this.x = 3;
41221                 this.y = 1;
41222                 break;
41223                         
41224             default :
41225                 break;
41226         }
41227         
41228         if(Roo.isTouch){
41229             this.el.on('touchstart', this.onTouchStart, this);
41230             this.el.on('touchmove', this.onTouchMove, this);
41231             this.el.on('touchend', this.onTouchEnd, this);
41232             this.el.on('contextmenu', this.onContextMenu, this);
41233         } else {
41234             this.el.on('mouseenter'  ,this.enter, this);
41235             this.el.on('mouseleave', this.leave, this);
41236             this.el.on('click', this.onClick, this);
41237         }
41238         
41239         if (typeof(this.parent().bricks) == 'object' && this.parent().bricks != null) {
41240             this.parent().bricks.push(this);   
41241         }
41242         
41243     },
41244     
41245     onClick: function(e, el)
41246     {
41247         var time = this.endTimer - this.startTimer;
41248         // Roo.log(e.preventDefault());
41249         if(Roo.isTouch){
41250             if(time > 1000){
41251                 e.preventDefault();
41252                 return;
41253             }
41254         }
41255         
41256         if(!this.preventDefault){
41257             return;
41258         }
41259         
41260         e.preventDefault();
41261         
41262         if (this.activeClass != '') {
41263             this.selectBrick();
41264         }
41265         
41266         this.fireEvent('click', this, e);
41267     },
41268     
41269     enter: function(e, el)
41270     {
41271         e.preventDefault();
41272         
41273         if(!this.isFitContainer || this.maskInverse || this.videourl.length){
41274             return;
41275         }
41276         
41277         if(this.bgimage.length && this.html.length){
41278             this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
41279         }
41280     },
41281     
41282     leave: function(e, el)
41283     {
41284         e.preventDefault();
41285         
41286         if(!this.isFitContainer || this.maskInverse  || this.videourl.length){
41287             return;
41288         }
41289         
41290         if(this.bgimage.length && this.html.length){
41291             this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
41292         }
41293     },
41294     
41295     onTouchStart: function(e, el)
41296     {
41297 //        e.preventDefault();
41298         
41299         this.touchmoved = false;
41300         
41301         if(!this.isFitContainer){
41302             return;
41303         }
41304         
41305         if(!this.bgimage.length || !this.html.length){
41306             return;
41307         }
41308         
41309         this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
41310         
41311         this.timer = new Date().getTime();
41312         
41313     },
41314     
41315     onTouchMove: function(e, el)
41316     {
41317         this.touchmoved = true;
41318     },
41319     
41320     onContextMenu : function(e,el)
41321     {
41322         e.preventDefault();
41323         e.stopPropagation();
41324         return false;
41325     },
41326     
41327     onTouchEnd: function(e, el)
41328     {
41329 //        e.preventDefault();
41330         
41331         if((new Date().getTime() - this.timer > 1000) || !this.href.length || this.touchmoved){
41332         
41333             this.leave(e,el);
41334             
41335             return;
41336         }
41337         
41338         if(!this.bgimage.length || !this.html.length){
41339             
41340             if(this.href.length){
41341                 window.location.href = this.href;
41342             }
41343             
41344             return;
41345         }
41346         
41347         if(!this.isFitContainer){
41348             return;
41349         }
41350         
41351         this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
41352         
41353         window.location.href = this.href;
41354     },
41355     
41356     //selection on single brick only
41357     selectBrick : function() {
41358         
41359         if (!this.parentId) {
41360             return;
41361         }
41362         
41363         var m = Roo.bootstrap.LayoutMasonry.get(this.parentId);
41364         var index = m.selectedBrick.indexOf(this.id);
41365         
41366         if ( index > -1) {
41367             m.selectedBrick.splice(index,1);
41368             this.el.removeClass(this.activeClass);
41369             return;
41370         }
41371         
41372         for(var i = 0; i < m.selectedBrick.length; i++) {
41373             var b = Roo.bootstrap.MasonryBrick.get(m.selectedBrick[i]);
41374             b.el.removeClass(b.activeClass);
41375         }
41376         
41377         m.selectedBrick = [];
41378         
41379         m.selectedBrick.push(this.id);
41380         this.el.addClass(this.activeClass);
41381         return;
41382     },
41383     
41384     isSelected : function(){
41385         return this.el.hasClass(this.activeClass);
41386         
41387     }
41388 });
41389
41390 Roo.apply(Roo.bootstrap.MasonryBrick, {
41391     
41392     //groups: {},
41393     groups : new Roo.util.MixedCollection(false, function(o) { return o.el.id; }),
41394      /**
41395     * register a Masonry Brick
41396     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
41397     */
41398     
41399     register : function(brick)
41400     {
41401         //this.groups[brick.id] = brick;
41402         this.groups.add(brick.id, brick);
41403     },
41404     /**
41405     * fetch a  masonry brick based on the masonry brick ID
41406     * @param {string} the masonry brick to add
41407     * @returns {Roo.bootstrap.MasonryBrick} the masonry brick
41408     */
41409     
41410     get: function(brick_id) 
41411     {
41412         // if (typeof(this.groups[brick_id]) == 'undefined') {
41413         //     return false;
41414         // }
41415         // return this.groups[brick_id] ;
41416         
41417         if(this.groups.key(brick_id)) {
41418             return this.groups.key(brick_id);
41419         }
41420         
41421         return false;
41422     }
41423     
41424     
41425     
41426 });
41427
41428  /*
41429  * - LGPL
41430  *
41431  * element
41432  * 
41433  */
41434
41435 /**
41436  * @class Roo.bootstrap.Brick
41437  * @extends Roo.bootstrap.Component
41438  * Bootstrap Brick class
41439  * 
41440  * @constructor
41441  * Create a new Brick
41442  * @param {Object} config The config object
41443  */
41444
41445 Roo.bootstrap.Brick = function(config){
41446     Roo.bootstrap.Brick.superclass.constructor.call(this, config);
41447     
41448     this.addEvents({
41449         // raw events
41450         /**
41451          * @event click
41452          * When a Brick is click
41453          * @param {Roo.bootstrap.Brick} this
41454          * @param {Roo.EventObject} e
41455          */
41456         "click" : true
41457     });
41458 };
41459
41460 Roo.extend(Roo.bootstrap.Brick, Roo.bootstrap.Component,  {
41461     
41462     /**
41463      * @cfg {String} title
41464      */   
41465     title : '',
41466     /**
41467      * @cfg {String} html
41468      */   
41469     html : '',
41470     /**
41471      * @cfg {String} bgimage
41472      */   
41473     bgimage : '',
41474     /**
41475      * @cfg {String} cls
41476      */   
41477     cls : '',
41478     /**
41479      * @cfg {String} href
41480      */   
41481     href : '',
41482     /**
41483      * @cfg {String} video
41484      */   
41485     video : '',
41486     /**
41487      * @cfg {Boolean} square
41488      */   
41489     square : true,
41490     
41491     getAutoCreate : function()
41492     {
41493         var cls = 'roo-brick';
41494         
41495         if(this.href.length){
41496             cls += ' roo-brick-link';
41497         }
41498         
41499         if(this.bgimage.length){
41500             cls += ' roo-brick-image';
41501         }
41502         
41503         if(!this.html.length && !this.bgimage.length){
41504             cls += ' roo-brick-center-title';
41505         }
41506         
41507         if(!this.html.length && this.bgimage.length){
41508             cls += ' roo-brick-bottom-title';
41509         }
41510         
41511         if(this.cls){
41512             cls += ' ' + this.cls;
41513         }
41514         
41515         var cfg = {
41516             tag: (this.href.length) ? 'a' : 'div',
41517             cls: cls,
41518             cn: [
41519                 {
41520                     tag: 'div',
41521                     cls: 'roo-brick-paragraph',
41522                     cn: []
41523                 }
41524             ]
41525         };
41526         
41527         if(this.href.length){
41528             cfg.href = this.href;
41529         }
41530         
41531         var cn = cfg.cn[0].cn;
41532         
41533         if(this.title.length){
41534             cn.push({
41535                 tag: 'h4',
41536                 cls: 'roo-brick-title',
41537                 html: this.title
41538             });
41539         }
41540         
41541         if(this.html.length){
41542             cn.push({
41543                 tag: 'p',
41544                 cls: 'roo-brick-text',
41545                 html: this.html
41546             });
41547         } else {
41548             cn.cls += ' hide';
41549         }
41550         
41551         if(this.bgimage.length){
41552             cfg.cn.push({
41553                 tag: 'img',
41554                 cls: 'roo-brick-image-view',
41555                 src: this.bgimage
41556             });
41557         }
41558         
41559         return cfg;
41560     },
41561     
41562     initEvents: function() 
41563     {
41564         if(this.title.length || this.html.length){
41565             this.el.on('mouseenter'  ,this.enter, this);
41566             this.el.on('mouseleave', this.leave, this);
41567         }
41568         
41569         Roo.EventManager.onWindowResize(this.resize, this); 
41570         
41571         if(this.bgimage.length){
41572             this.imageEl = this.el.select('.roo-brick-image-view', true).first();
41573             this.imageEl.on('load', this.onImageLoad, this);
41574             return;
41575         }
41576         
41577         this.resize();
41578     },
41579     
41580     onImageLoad : function()
41581     {
41582         this.resize();
41583     },
41584     
41585     resize : function()
41586     {
41587         var paragraph = this.el.select('.roo-brick-paragraph', true).first();
41588         
41589         paragraph.setHeight(paragraph.getWidth() + paragraph.getPadding('tb'));
41590         
41591         if(this.bgimage.length){
41592             var image = this.el.select('.roo-brick-image-view', true).first();
41593             
41594             image.setWidth(paragraph.getWidth());
41595             
41596             if(this.square){
41597                 image.setHeight(paragraph.getWidth());
41598             }
41599             
41600             this.el.setHeight(image.getHeight());
41601             paragraph.setHeight(image.getHeight());
41602             
41603         }
41604         
41605     },
41606     
41607     enter: function(e, el)
41608     {
41609         e.preventDefault();
41610         
41611         if(this.bgimage.length){
41612             this.el.select('.roo-brick-paragraph', true).first().setOpacity(0.9, true);
41613             this.el.select('.roo-brick-image-view', true).first().setOpacity(0.1, true);
41614         }
41615     },
41616     
41617     leave: function(e, el)
41618     {
41619         e.preventDefault();
41620         
41621         if(this.bgimage.length){
41622             this.el.select('.roo-brick-paragraph', true).first().setOpacity(0, true);
41623             this.el.select('.roo-brick-image-view', true).first().setOpacity(1, true);
41624         }
41625     }
41626     
41627 });
41628
41629  
41630
41631  /*
41632  * - LGPL
41633  *
41634  * Number field 
41635  */
41636
41637 /**
41638  * @class Roo.bootstrap.form.NumberField
41639  * @extends Roo.bootstrap.form.Input
41640  * Bootstrap NumberField class
41641  * 
41642  * 
41643  * 
41644  * 
41645  * @constructor
41646  * Create a new NumberField
41647  * @param {Object} config The config object
41648  */
41649
41650 Roo.bootstrap.form.NumberField = function(config){
41651     Roo.bootstrap.form.NumberField.superclass.constructor.call(this, config);
41652 };
41653
41654 Roo.extend(Roo.bootstrap.form.NumberField, Roo.bootstrap.form.Input, {
41655     
41656     /**
41657      * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
41658      */
41659     allowDecimals : true,
41660     /**
41661      * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
41662      */
41663     decimalSeparator : ".",
41664     /**
41665      * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
41666      */
41667     decimalPrecision : 2,
41668     /**
41669      * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
41670      */
41671     allowNegative : true,
41672     
41673     /**
41674      * @cfg {Boolean} allowZero False to blank out if the user enters '0' (defaults to true)
41675      */
41676     allowZero: true,
41677     /**
41678      * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
41679      */
41680     minValue : Number.NEGATIVE_INFINITY,
41681     /**
41682      * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
41683      */
41684     maxValue : Number.MAX_VALUE,
41685     /**
41686      * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
41687      */
41688     minText : "The minimum value for this field is {0}",
41689     /**
41690      * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
41691      */
41692     maxText : "The maximum value for this field is {0}",
41693     /**
41694      * @cfg {String} nanText Error text to display if the value is not a valid number.  For example, this can happen
41695      * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
41696      */
41697     nanText : "{0} is not a valid number",
41698     /**
41699      * @cfg {String} thousandsDelimiter Symbol of thousandsDelimiter
41700      */
41701     thousandsDelimiter : false,
41702     /**
41703      * @cfg {String} valueAlign alignment of value
41704      */
41705     valueAlign : "left",
41706
41707     getAutoCreate : function()
41708     {
41709         var hiddenInput = {
41710             tag: 'input',
41711             type: 'hidden',
41712             id: Roo.id(),
41713             cls: 'hidden-number-input'
41714         };
41715         
41716         if (this.name) {
41717             hiddenInput.name = this.name;
41718         }
41719         
41720         this.name = '';
41721         
41722         var cfg = Roo.bootstrap.form.NumberField.superclass.getAutoCreate.call(this);
41723         
41724         this.name = hiddenInput.name;
41725         
41726         if(cfg.cn.length > 0) {
41727             cfg.cn.push(hiddenInput);
41728         }
41729         
41730         return cfg;
41731     },
41732
41733     // private
41734     initEvents : function()
41735     {   
41736         Roo.bootstrap.form.NumberField.superclass.initEvents.call(this);
41737         
41738         var allowed = "0123456789";
41739         
41740         if(this.allowDecimals){
41741             allowed += this.decimalSeparator;
41742         }
41743         
41744         if(this.allowNegative){
41745             allowed += "-";
41746         }
41747         
41748         if(this.thousandsDelimiter) {
41749             allowed += ",";
41750         }
41751         
41752         this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
41753         
41754         var keyPress = function(e){
41755             
41756             var k = e.getKey();
41757             
41758             var c = e.getCharCode();
41759             
41760             if(
41761                     (String.fromCharCode(c) == '.' || String.fromCharCode(c) == '-') &&
41762                     allowed.indexOf(String.fromCharCode(c)) === -1
41763             ){
41764                 e.stopEvent();
41765                 return;
41766             }
41767             
41768             if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
41769                 return;
41770             }
41771             
41772             if(allowed.indexOf(String.fromCharCode(c)) === -1){
41773                 e.stopEvent();
41774             }
41775         };
41776         
41777         this.el.on("keypress", keyPress, this);
41778     },
41779     
41780     validateValue : function(value)
41781     {
41782         
41783         if(!Roo.bootstrap.form.NumberField.superclass.validateValue.call(this, value)){
41784             return false;
41785         }
41786         
41787         var num = this.parseValue(value);
41788         
41789         if(isNaN(num)){
41790             this.markInvalid(String.format(this.nanText, value));
41791             return false;
41792         }
41793         
41794         if(num < this.minValue){
41795             this.markInvalid(String.format(this.minText, this.minValue));
41796             return false;
41797         }
41798         
41799         if(num > this.maxValue){
41800             this.markInvalid(String.format(this.maxText, this.maxValue));
41801             return false;
41802         }
41803         
41804         return true;
41805     },
41806
41807     getValue : function()
41808     {
41809         var v = this.hiddenEl().getValue();
41810         
41811         return this.fixPrecision(this.parseValue(v));
41812     },
41813
41814     parseValue : function(value)
41815     {
41816         if(this.thousandsDelimiter) {
41817             value += "";
41818             r = new RegExp(",", "g");
41819             value = value.replace(r, "");
41820         }
41821         
41822         value = parseFloat(String(value).replace(this.decimalSeparator, "."));
41823         return isNaN(value) ? '' : value;
41824     },
41825
41826     fixPrecision : function(value)
41827     {
41828         if(this.thousandsDelimiter) {
41829             value += "";
41830             r = new RegExp(",", "g");
41831             value = value.replace(r, "");
41832         }
41833         
41834         var nan = isNaN(value);
41835         
41836         if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
41837             return nan ? '' : value;
41838         }
41839         return parseFloat(value).toFixed(this.decimalPrecision);
41840     },
41841
41842     setValue : function(v)
41843     {
41844         v = String(this.fixPrecision(v)).replace(".", this.decimalSeparator);
41845         
41846         this.value = v;
41847         
41848         if(this.rendered){
41849             
41850             this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
41851             
41852             this.inputEl().dom.value = (v == '') ? '' :
41853                 Roo.util.Format.number(v, this.decimalPrecision, this.thousandsDelimiter || '');
41854             
41855             if(!this.allowZero && v === '0') {
41856                 this.hiddenEl().dom.value = '';
41857                 this.inputEl().dom.value = '';
41858             }
41859             
41860             this.validate();
41861         }
41862     },
41863
41864     decimalPrecisionFcn : function(v)
41865     {
41866         return Math.floor(v);
41867     },
41868
41869     beforeBlur : function()
41870     {
41871         var v = this.parseValue(this.getRawValue());
41872         
41873         if(v || v === 0 || v === ''){
41874             this.setValue(v);
41875         }
41876     },
41877     
41878     hiddenEl : function()
41879     {
41880         return this.el.select('input.hidden-number-input',true).first();
41881     }
41882     
41883 });
41884
41885  
41886
41887 /*
41888 * Licence: LGPL
41889 */
41890
41891 /**
41892  * @class Roo.bootstrap.DocumentSlider
41893  * @extends Roo.bootstrap.Component
41894  * Bootstrap DocumentSlider class
41895  * 
41896  * @constructor
41897  * Create a new DocumentViewer
41898  * @param {Object} config The config object
41899  */
41900
41901 Roo.bootstrap.DocumentSlider = function(config){
41902     Roo.bootstrap.DocumentSlider.superclass.constructor.call(this, config);
41903     
41904     this.files = [];
41905     
41906     this.addEvents({
41907         /**
41908          * @event initial
41909          * Fire after initEvent
41910          * @param {Roo.bootstrap.DocumentSlider} this
41911          */
41912         "initial" : true,
41913         /**
41914          * @event update
41915          * Fire after update
41916          * @param {Roo.bootstrap.DocumentSlider} this
41917          */
41918         "update" : true,
41919         /**
41920          * @event click
41921          * Fire after click
41922          * @param {Roo.bootstrap.DocumentSlider} this
41923          */
41924         "click" : true
41925     });
41926 };
41927
41928 Roo.extend(Roo.bootstrap.DocumentSlider, Roo.bootstrap.Component,  {
41929     
41930     files : false,
41931     
41932     indicator : 0,
41933     
41934     getAutoCreate : function()
41935     {
41936         var cfg = {
41937             tag : 'div',
41938             cls : 'roo-document-slider',
41939             cn : [
41940                 {
41941                     tag : 'div',
41942                     cls : 'roo-document-slider-header',
41943                     cn : [
41944                         {
41945                             tag : 'div',
41946                             cls : 'roo-document-slider-header-title'
41947                         }
41948                     ]
41949                 },
41950                 {
41951                     tag : 'div',
41952                     cls : 'roo-document-slider-body',
41953                     cn : [
41954                         {
41955                             tag : 'div',
41956                             cls : 'roo-document-slider-prev',
41957                             cn : [
41958                                 {
41959                                     tag : 'i',
41960                                     cls : 'fa fa-chevron-left'
41961                                 }
41962                             ]
41963                         },
41964                         {
41965                             tag : 'div',
41966                             cls : 'roo-document-slider-thumb',
41967                             cn : [
41968                                 {
41969                                     tag : 'img',
41970                                     cls : 'roo-document-slider-image'
41971                                 }
41972                             ]
41973                         },
41974                         {
41975                             tag : 'div',
41976                             cls : 'roo-document-slider-next',
41977                             cn : [
41978                                 {
41979                                     tag : 'i',
41980                                     cls : 'fa fa-chevron-right'
41981                                 }
41982                             ]
41983                         }
41984                     ]
41985                 }
41986             ]
41987         };
41988         
41989         return cfg;
41990     },
41991     
41992     initEvents : function()
41993     {
41994         this.headerEl = this.el.select('.roo-document-slider-header', true).first();
41995         this.headerEl.setVisibilityMode(Roo.Element.DISPLAY);
41996         
41997         this.titleEl = this.el.select('.roo-document-slider-header .roo-document-slider-header-title', true).first();
41998         this.titleEl.setVisibilityMode(Roo.Element.DISPLAY);
41999         
42000         this.bodyEl = this.el.select('.roo-document-slider-body', true).first();
42001         this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY);
42002         
42003         this.thumbEl = this.el.select('.roo-document-slider-thumb', true).first();
42004         this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY);
42005         
42006         this.imageEl = this.el.select('.roo-document-slider-image', true).first();
42007         this.imageEl.setVisibilityMode(Roo.Element.DISPLAY);
42008         
42009         this.prevIndicator = this.el.select('.roo-document-slider-prev i', true).first();
42010         this.prevIndicator.setVisibilityMode(Roo.Element.DISPLAY);
42011         
42012         this.nextIndicator = this.el.select('.roo-document-slider-next i', true).first();
42013         this.nextIndicator.setVisibilityMode(Roo.Element.DISPLAY);
42014         
42015         this.thumbEl.on('click', this.onClick, this);
42016         
42017         this.prevIndicator.on('click', this.prev, this);
42018         
42019         this.nextIndicator.on('click', this.next, this);
42020         
42021     },
42022     
42023     initial : function()
42024     {
42025         if(this.files.length){
42026             this.indicator = 1;
42027             this.update()
42028         }
42029         
42030         this.fireEvent('initial', this);
42031     },
42032     
42033     update : function()
42034     {
42035         this.imageEl.attr('src', this.files[this.indicator - 1]);
42036         
42037         this.titleEl.dom.innerHTML = String.format('{0} / {1}', this.indicator, this.files.length);
42038         
42039         this.prevIndicator.show();
42040         
42041         if(this.indicator == 1){
42042             this.prevIndicator.hide();
42043         }
42044         
42045         this.nextIndicator.show();
42046         
42047         if(this.indicator == this.files.length){
42048             this.nextIndicator.hide();
42049         }
42050         
42051         this.thumbEl.scrollTo('top');
42052         
42053         this.fireEvent('update', this);
42054     },
42055     
42056     onClick : function(e)
42057     {
42058         e.preventDefault();
42059         
42060         this.fireEvent('click', this);
42061     },
42062     
42063     prev : function(e)
42064     {
42065         e.preventDefault();
42066         
42067         this.indicator = Math.max(1, this.indicator - 1);
42068         
42069         this.update();
42070     },
42071     
42072     next : function(e)
42073     {
42074         e.preventDefault();
42075         
42076         this.indicator = Math.min(this.files.length, this.indicator + 1);
42077         
42078         this.update();
42079     }
42080 });
42081 /*
42082  * - LGPL
42083  *
42084  * RadioSet
42085  *
42086  *
42087  */
42088
42089 /**
42090  * @class Roo.bootstrap.form.RadioSet
42091  * @extends Roo.bootstrap.form.Input
42092  * @children Roo.bootstrap.form.Radio
42093  * Bootstrap RadioSet class
42094  * @cfg {String} indicatorpos (left|right) default left
42095  * @cfg {Boolean} inline (true|false) inline the element (default true)
42096  * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the radio
42097  * @constructor
42098  * Create a new RadioSet
42099  * @param {Object} config The config object
42100  */
42101
42102 Roo.bootstrap.form.RadioSet = function(config){
42103     
42104     Roo.bootstrap.form.RadioSet.superclass.constructor.call(this, config);
42105     
42106     this.radioes = [];
42107     
42108     Roo.bootstrap.form.RadioSet.register(this);
42109     
42110     this.addEvents({
42111         /**
42112         * @event check
42113         * Fires when the element is checked or unchecked.
42114         * @param {Roo.bootstrap.form.RadioSet} this This radio
42115         * @param {Roo.bootstrap.form.Radio} item The checked item
42116         */
42117        check : true,
42118        /**
42119         * @event click
42120         * Fires when the element is click.
42121         * @param {Roo.bootstrap.form.RadioSet} this This radio set
42122         * @param {Roo.bootstrap.form.Radio} item The checked item
42123         * @param {Roo.EventObject} e The event object
42124         */
42125        click : true
42126     });
42127     
42128 };
42129
42130 Roo.extend(Roo.bootstrap.form.RadioSet, Roo.bootstrap.form.Input,  {
42131
42132     radioes : false,
42133     
42134     inline : true,
42135     
42136     weight : '',
42137     
42138     indicatorpos : 'left',
42139     
42140     getAutoCreate : function()
42141     {
42142         var label = {
42143             tag : 'label',
42144             cls : 'roo-radio-set-label',
42145             cn : [
42146                 {
42147                     tag : 'span',
42148                     html : this.fieldLabel
42149                 }
42150             ]
42151         };
42152         if (Roo.bootstrap.version == 3) {
42153             
42154             
42155             if(this.indicatorpos == 'left'){
42156                 label.cn.unshift({
42157                     tag : 'i',
42158                     cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
42159                     tooltip : 'This field is required'
42160                 });
42161             } else {
42162                 label.cn.push({
42163                     tag : 'i',
42164                     cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
42165                     tooltip : 'This field is required'
42166                 });
42167             }
42168         }
42169         var items = {
42170             tag : 'div',
42171             cls : 'roo-radio-set-items'
42172         };
42173         
42174         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
42175         
42176         if (align === 'left' && this.fieldLabel.length) {
42177             
42178             items = {
42179                 cls : "roo-radio-set-right", 
42180                 cn: [
42181                     items
42182                 ]
42183             };
42184             
42185             if(this.labelWidth > 12){
42186                 label.style = "width: " + this.labelWidth + 'px';
42187             }
42188             
42189             if(this.labelWidth < 13 && this.labelmd == 0){
42190                 this.labelmd = this.labelWidth;
42191             }
42192             
42193             if(this.labellg > 0){
42194                 label.cls += ' col-lg-' + this.labellg;
42195                 items.cls += ' col-lg-' + (12 - this.labellg);
42196             }
42197             
42198             if(this.labelmd > 0){
42199                 label.cls += ' col-md-' + this.labelmd;
42200                 items.cls += ' col-md-' + (12 - this.labelmd);
42201             }
42202             
42203             if(this.labelsm > 0){
42204                 label.cls += ' col-sm-' + this.labelsm;
42205                 items.cls += ' col-sm-' + (12 - this.labelsm);
42206             }
42207             
42208             if(this.labelxs > 0){
42209                 label.cls += ' col-xs-' + this.labelxs;
42210                 items.cls += ' col-xs-' + (12 - this.labelxs);
42211             }
42212         }
42213         
42214         var cfg = {
42215             tag : 'div',
42216             cls : 'roo-radio-set',
42217             cn : [
42218                 {
42219                     tag : 'input',
42220                     cls : 'roo-radio-set-input',
42221                     type : 'hidden',
42222                     name : this.name,
42223                     value : this.value ? this.value :  ''
42224                 },
42225                 label,
42226                 items
42227             ]
42228         };
42229         
42230         if(this.weight.length){
42231             cfg.cls += ' roo-radio-' + this.weight;
42232         }
42233         
42234         if(this.inline) {
42235             cfg.cls += ' roo-radio-set-inline';
42236         }
42237         
42238         var settings=this;
42239         ['xs','sm','md','lg'].map(function(size){
42240             if (settings[size]) {
42241                 cfg.cls += ' col-' + size + '-' + settings[size];
42242             }
42243         });
42244         
42245         return cfg;
42246         
42247     },
42248
42249     initEvents : function()
42250     {
42251         this.labelEl = this.el.select('.roo-radio-set-label', true).first();
42252         this.labelEl.setVisibilityMode(Roo.Element.DISPLAY);
42253         
42254         if(!this.fieldLabel.length){
42255             this.labelEl.hide();
42256         }
42257         
42258         this.itemsEl = this.el.select('.roo-radio-set-items', true).first();
42259         this.itemsEl.setVisibilityMode(Roo.Element.DISPLAY);
42260         
42261         this.indicator = this.indicatorEl();
42262         
42263         if(this.indicator){
42264             this.indicator.addClass('invisible');
42265         }
42266         
42267         this.originalValue = this.getValue();
42268         
42269     },
42270     
42271     inputEl: function ()
42272     {
42273         return this.el.select('.roo-radio-set-input', true).first();
42274     },
42275     
42276     getChildContainer : function()
42277     {
42278         return this.itemsEl;
42279     },
42280     
42281     register : function(item)
42282     {
42283         this.radioes.push(item);
42284         
42285     },
42286     
42287     validate : function()
42288     {   
42289         if(this.getVisibilityEl().hasClass('hidden')){
42290             return true;
42291         }
42292         
42293         var valid = false;
42294         
42295         Roo.each(this.radioes, function(i){
42296             if(!i.checked){
42297                 return;
42298             }
42299             
42300             valid = true;
42301             return false;
42302         });
42303         
42304         if(this.allowBlank) {
42305             return true;
42306         }
42307         
42308         if(this.disabled || valid){
42309             this.markValid();
42310             return true;
42311         }
42312         
42313         this.markInvalid();
42314         return false;
42315         
42316     },
42317     
42318     markValid : function()
42319     {
42320         if(this.labelEl.isVisible(true) && this.indicatorEl()){
42321             this.indicatorEl().removeClass('visible');
42322             this.indicatorEl().addClass('invisible');
42323         }
42324         
42325         
42326         if (Roo.bootstrap.version == 3) {
42327             this.el.removeClass([this.invalidClass, this.validClass]);
42328             this.el.addClass(this.validClass);
42329         } else {
42330             this.el.removeClass(['is-invalid','is-valid']);
42331             this.el.addClass(['is-valid']);
42332         }
42333         this.fireEvent('valid', this);
42334     },
42335     
42336     markInvalid : function(msg)
42337     {
42338         if(this.allowBlank || this.disabled){
42339             return;
42340         }
42341         
42342         if(this.labelEl.isVisible(true) && this.indicatorEl()){
42343             this.indicatorEl().removeClass('invisible');
42344             this.indicatorEl().addClass('visible');
42345         }
42346         if (Roo.bootstrap.version == 3) {
42347             this.el.removeClass([this.invalidClass, this.validClass]);
42348             this.el.addClass(this.invalidClass);
42349         } else {
42350             this.el.removeClass(['is-invalid','is-valid']);
42351             this.el.addClass(['is-invalid']);
42352         }
42353         
42354         this.fireEvent('invalid', this, msg);
42355         
42356     },
42357     
42358     setValue : function(v, suppressEvent)
42359     {   
42360         if(this.value === v){
42361             return;
42362         }
42363         
42364         this.value = v;
42365         
42366         if(this.rendered){
42367             this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
42368         }
42369         
42370         Roo.each(this.radioes, function(i){
42371             i.checked = false;
42372             i.el.removeClass('checked');
42373         });
42374         
42375         Roo.each(this.radioes, function(i){
42376             
42377             if(i.value === v || i.value.toString() === v.toString()){
42378                 i.checked = true;
42379                 i.el.addClass('checked');
42380                 
42381                 if(suppressEvent !== true){
42382                     this.fireEvent('check', this, i);
42383                 }
42384                 
42385                 return false;
42386             }
42387             
42388         }, this);
42389         
42390         this.validate();
42391     },
42392     
42393     clearInvalid : function(){
42394         
42395         if(!this.el || this.preventMark){
42396             return;
42397         }
42398         
42399         this.el.removeClass([this.invalidClass]);
42400         
42401         this.fireEvent('valid', this);
42402     }
42403     
42404 });
42405
42406 Roo.apply(Roo.bootstrap.form.RadioSet, {
42407     
42408     groups: {},
42409     
42410     register : function(set)
42411     {
42412         this.groups[set.name] = set;
42413     },
42414     
42415     get: function(name) 
42416     {
42417         if (typeof(this.groups[name]) == 'undefined') {
42418             return false;
42419         }
42420         
42421         return this.groups[name] ;
42422     }
42423     
42424 });
42425 /*
42426  * Based on:
42427  * Ext JS Library 1.1.1
42428  * Copyright(c) 2006-2007, Ext JS, LLC.
42429  *
42430  * Originally Released Under LGPL - original licence link has changed is not relivant.
42431  *
42432  * Fork - LGPL
42433  * <script type="text/javascript">
42434  */
42435
42436
42437 /**
42438  * @class Roo.bootstrap.SplitBar
42439  * @extends Roo.util.Observable
42440  * Creates draggable splitter bar functionality from two elements (element to be dragged and element to be resized).
42441  * <br><br>
42442  * Usage:
42443  * <pre><code>
42444 var split = new Roo.bootstrap.SplitBar("elementToDrag", "elementToSize",
42445                    Roo.bootstrap.SplitBar.HORIZONTAL, Roo.bootstrap.SplitBar.LEFT);
42446 split.setAdapter(new Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter("container"));
42447 split.minSize = 100;
42448 split.maxSize = 600;
42449 split.animate = true;
42450 split.on('moved', splitterMoved);
42451 </code></pre>
42452  * @constructor
42453  * Create a new SplitBar
42454  * @config {String/HTMLElement/Roo.Element} dragElement The element to be dragged and act as the SplitBar. 
42455  * @config {String/HTMLElement/Roo.Element} resizingElement The element to be resized based on where the SplitBar element is dragged 
42456  * @config {Number} orientation (optional) Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
42457  * @config {Number} placement (optional) Either Roo.bootstrap.SplitBar.LEFT or Roo.bootstrap.SplitBar.RIGHT for horizontal or  
42458                         Roo.bootstrap.SplitBar.TOP or Roo.bootstrap.SplitBar.BOTTOM for vertical. (By default, this is determined automatically by the initial
42459                         position of the SplitBar).
42460  */
42461 Roo.bootstrap.SplitBar = function(cfg){
42462     
42463     /** @private */
42464     
42465     //{
42466     //  dragElement : elm
42467     //  resizingElement: el,
42468         // optional..
42469     //    orientation : Either Roo.bootstrap.SplitBar.HORIZONTAL
42470     //    placement : Roo.bootstrap.SplitBar.LEFT  ,
42471         // existingProxy ???
42472     //}
42473     
42474     this.el = Roo.get(cfg.dragElement, true);
42475     this.el.dom.unselectable = "on";
42476     /** @private */
42477     this.resizingEl = Roo.get(cfg.resizingElement, true);
42478
42479     /**
42480      * @private
42481      * The orientation of the split. Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
42482      * Note: If this is changed after creating the SplitBar, the placement property must be manually updated
42483      * @type Number
42484      */
42485     this.orientation = cfg.orientation || Roo.bootstrap.SplitBar.HORIZONTAL;
42486     
42487     /**
42488      * The minimum size of the resizing element. (Defaults to 0)
42489      * @type Number
42490      */
42491     this.minSize = 0;
42492     
42493     /**
42494      * The maximum size of the resizing element. (Defaults to 2000)
42495      * @type Number
42496      */
42497     this.maxSize = 2000;
42498     
42499     /**
42500      * Whether to animate the transition to the new size
42501      * @type Boolean
42502      */
42503     this.animate = false;
42504     
42505     /**
42506      * Whether to create a transparent shim that overlays the page when dragging, enables dragging across iframes.
42507      * @type Boolean
42508      */
42509     this.useShim = false;
42510     
42511     /** @private */
42512     this.shim = null;
42513     
42514     if(!cfg.existingProxy){
42515         /** @private */
42516         this.proxy = Roo.bootstrap.SplitBar.createProxy(this.orientation);
42517     }else{
42518         this.proxy = Roo.get(cfg.existingProxy).dom;
42519     }
42520     /** @private */
42521     this.dd = new Roo.dd.DDProxy(this.el.dom.id, "XSplitBars", {dragElId : this.proxy.id});
42522     
42523     /** @private */
42524     this.dd.b4StartDrag = this.onStartProxyDrag.createDelegate(this);
42525     
42526     /** @private */
42527     this.dd.endDrag = this.onEndProxyDrag.createDelegate(this);
42528     
42529     /** @private */
42530     this.dragSpecs = {};
42531     
42532     /**
42533      * @private The adapter to use to positon and resize elements
42534      */
42535     this.adapter = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
42536     this.adapter.init(this);
42537     
42538     if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
42539         /** @private */
42540         this.placement = cfg.placement || (this.el.getX() > this.resizingEl.getX() ? Roo.bootstrap.SplitBar.LEFT : Roo.bootstrap.SplitBar.RIGHT);
42541         this.el.addClass("roo-splitbar-h");
42542     }else{
42543         /** @private */
42544         this.placement = cfg.placement || (this.el.getY() > this.resizingEl.getY() ? Roo.bootstrap.SplitBar.TOP : Roo.bootstrap.SplitBar.BOTTOM);
42545         this.el.addClass("roo-splitbar-v");
42546     }
42547     
42548     this.addEvents({
42549         /**
42550          * @event resize
42551          * Fires when the splitter is moved (alias for {@link #event-moved})
42552          * @param {Roo.bootstrap.SplitBar} this
42553          * @param {Number} newSize the new width or height
42554          */
42555         "resize" : true,
42556         /**
42557          * @event moved
42558          * Fires when the splitter is moved
42559          * @param {Roo.bootstrap.SplitBar} this
42560          * @param {Number} newSize the new width or height
42561          */
42562         "moved" : true,
42563         /**
42564          * @event beforeresize
42565          * Fires before the splitter is dragged
42566          * @param {Roo.bootstrap.SplitBar} this
42567          */
42568         "beforeresize" : true,
42569
42570         "beforeapply" : true
42571     });
42572
42573     Roo.util.Observable.call(this);
42574 };
42575
42576 Roo.extend(Roo.bootstrap.SplitBar, Roo.util.Observable, {
42577     onStartProxyDrag : function(x, y){
42578         this.fireEvent("beforeresize", this);
42579         if(!this.overlay){
42580             var o = Roo.DomHelper.insertFirst(document.body,  {cls: "roo-drag-overlay", html: "&#160;"}, true);
42581             o.unselectable();
42582             o.enableDisplayMode("block");
42583             // all splitbars share the same overlay
42584             Roo.bootstrap.SplitBar.prototype.overlay = o;
42585         }
42586         this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
42587         this.overlay.show();
42588         Roo.get(this.proxy).setDisplayed("block");
42589         var size = this.adapter.getElementSize(this);
42590         this.activeMinSize = this.getMinimumSize();;
42591         this.activeMaxSize = this.getMaximumSize();;
42592         var c1 = size - this.activeMinSize;
42593         var c2 = Math.max(this.activeMaxSize - size, 0);
42594         if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
42595             this.dd.resetConstraints();
42596             this.dd.setXConstraint(
42597                 this.placement == Roo.bootstrap.SplitBar.LEFT ? c1 : c2, 
42598                 this.placement == Roo.bootstrap.SplitBar.LEFT ? c2 : c1
42599             );
42600             this.dd.setYConstraint(0, 0);
42601         }else{
42602             this.dd.resetConstraints();
42603             this.dd.setXConstraint(0, 0);
42604             this.dd.setYConstraint(
42605                 this.placement == Roo.bootstrap.SplitBar.TOP ? c1 : c2, 
42606                 this.placement == Roo.bootstrap.SplitBar.TOP ? c2 : c1
42607             );
42608          }
42609         this.dragSpecs.startSize = size;
42610         this.dragSpecs.startPoint = [x, y];
42611         Roo.dd.DDProxy.prototype.b4StartDrag.call(this.dd, x, y);
42612     },
42613     
42614     /** 
42615      * @private Called after the drag operation by the DDProxy
42616      */
42617     onEndProxyDrag : function(e){
42618         Roo.get(this.proxy).setDisplayed(false);
42619         var endPoint = Roo.lib.Event.getXY(e);
42620         if(this.overlay){
42621             this.overlay.hide();
42622         }
42623         var newSize;
42624         if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
42625             newSize = this.dragSpecs.startSize + 
42626                 (this.placement == Roo.bootstrap.SplitBar.LEFT ?
42627                     endPoint[0] - this.dragSpecs.startPoint[0] :
42628                     this.dragSpecs.startPoint[0] - endPoint[0]
42629                 );
42630         }else{
42631             newSize = this.dragSpecs.startSize + 
42632                 (this.placement == Roo.bootstrap.SplitBar.TOP ?
42633                     endPoint[1] - this.dragSpecs.startPoint[1] :
42634                     this.dragSpecs.startPoint[1] - endPoint[1]
42635                 );
42636         }
42637         newSize = Math.min(Math.max(newSize, this.activeMinSize), this.activeMaxSize);
42638         if(newSize != this.dragSpecs.startSize){
42639             if(this.fireEvent('beforeapply', this, newSize) !== false){
42640                 this.adapter.setElementSize(this, newSize);
42641                 this.fireEvent("moved", this, newSize);
42642                 this.fireEvent("resize", this, newSize);
42643             }
42644         }
42645     },
42646     
42647     /**
42648      * Get the adapter this SplitBar uses
42649      * @return The adapter object
42650      */
42651     getAdapter : function(){
42652         return this.adapter;
42653     },
42654     
42655     /**
42656      * Set the adapter this SplitBar uses
42657      * @param {Object} adapter A SplitBar adapter object
42658      */
42659     setAdapter : function(adapter){
42660         this.adapter = adapter;
42661         this.adapter.init(this);
42662     },
42663     
42664     /**
42665      * Gets the minimum size for the resizing element
42666      * @return {Number} The minimum size
42667      */
42668     getMinimumSize : function(){
42669         return this.minSize;
42670     },
42671     
42672     /**
42673      * Sets the minimum size for the resizing element
42674      * @param {Number} minSize The minimum size
42675      */
42676     setMinimumSize : function(minSize){
42677         this.minSize = minSize;
42678     },
42679     
42680     /**
42681      * Gets the maximum size for the resizing element
42682      * @return {Number} The maximum size
42683      */
42684     getMaximumSize : function(){
42685         return this.maxSize;
42686     },
42687     
42688     /**
42689      * Sets the maximum size for the resizing element
42690      * @param {Number} maxSize The maximum size
42691      */
42692     setMaximumSize : function(maxSize){
42693         this.maxSize = maxSize;
42694     },
42695     
42696     /**
42697      * Sets the initialize size for the resizing element
42698      * @param {Number} size The initial size
42699      */
42700     setCurrentSize : function(size){
42701         var oldAnimate = this.animate;
42702         this.animate = false;
42703         this.adapter.setElementSize(this, size);
42704         this.animate = oldAnimate;
42705     },
42706     
42707     /**
42708      * Destroy this splitbar. 
42709      * @param {Boolean} removeEl True to remove the element
42710      */
42711     destroy : function(removeEl){
42712         if(this.shim){
42713             this.shim.remove();
42714         }
42715         this.dd.unreg();
42716         this.proxy.parentNode.removeChild(this.proxy);
42717         if(removeEl){
42718             this.el.remove();
42719         }
42720     }
42721 });
42722
42723 /**
42724  * @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.
42725  */
42726 Roo.bootstrap.SplitBar.createProxy = function(dir){
42727     var proxy = new Roo.Element(document.createElement("div"));
42728     proxy.unselectable();
42729     var cls = 'roo-splitbar-proxy';
42730     proxy.addClass(cls + ' ' + (dir == Roo.bootstrap.SplitBar.HORIZONTAL ? cls +'-h' : cls + '-v'));
42731     document.body.appendChild(proxy.dom);
42732     return proxy.dom;
42733 };
42734
42735 /** 
42736  * @class Roo.bootstrap.SplitBar.BasicLayoutAdapter
42737  * Default Adapter. It assumes the splitter and resizing element are not positioned
42738  * elements and only gets/sets the width of the element. Generally used for table based layouts.
42739  */
42740 Roo.bootstrap.SplitBar.BasicLayoutAdapter = function(){
42741 };
42742
42743 Roo.bootstrap.SplitBar.BasicLayoutAdapter.prototype = {
42744     // do nothing for now
42745     init : function(s){
42746     
42747     },
42748     /**
42749      * Called before drag operations to get the current size of the resizing element. 
42750      * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
42751      */
42752      getElementSize : function(s){
42753         if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
42754             return s.resizingEl.getWidth();
42755         }else{
42756             return s.resizingEl.getHeight();
42757         }
42758     },
42759     
42760     /**
42761      * Called after drag operations to set the size of the resizing element.
42762      * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
42763      * @param {Number} newSize The new size to set
42764      * @param {Function} onComplete A function to be invoked when resizing is complete
42765      */
42766     setElementSize : function(s, newSize, onComplete){
42767         if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
42768             if(!s.animate){
42769                 s.resizingEl.setWidth(newSize);
42770                 if(onComplete){
42771                     onComplete(s, newSize);
42772                 }
42773             }else{
42774                 s.resizingEl.setWidth(newSize, true, .1, onComplete, 'easeOut');
42775             }
42776         }else{
42777             
42778             if(!s.animate){
42779                 s.resizingEl.setHeight(newSize);
42780                 if(onComplete){
42781                     onComplete(s, newSize);
42782                 }
42783             }else{
42784                 s.resizingEl.setHeight(newSize, true, .1, onComplete, 'easeOut');
42785             }
42786         }
42787     }
42788 };
42789
42790 /** 
42791  *@class Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter
42792  * @extends Roo.bootstrap.SplitBar.BasicLayoutAdapter
42793  * Adapter that  moves the splitter element to align with the resized sizing element. 
42794  * Used with an absolute positioned SplitBar.
42795  * @param {String/HTMLElement/Roo.Element} container The container that wraps around the absolute positioned content. If it's
42796  * document.body, make sure you assign an id to the body element.
42797  */
42798 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter = function(container){
42799     this.basic = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
42800     this.container = Roo.get(container);
42801 };
42802
42803 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter.prototype = {
42804     init : function(s){
42805         this.basic.init(s);
42806     },
42807     
42808     getElementSize : function(s){
42809         return this.basic.getElementSize(s);
42810     },
42811     
42812     setElementSize : function(s, newSize, onComplete){
42813         this.basic.setElementSize(s, newSize, this.moveSplitter.createDelegate(this, [s]));
42814     },
42815     
42816     moveSplitter : function(s){
42817         var yes = Roo.bootstrap.SplitBar;
42818         switch(s.placement){
42819             case yes.LEFT:
42820                 s.el.setX(s.resizingEl.getRight());
42821                 break;
42822             case yes.RIGHT:
42823                 s.el.setStyle("right", (this.container.getWidth() - s.resizingEl.getLeft()) + "px");
42824                 break;
42825             case yes.TOP:
42826                 s.el.setY(s.resizingEl.getBottom());
42827                 break;
42828             case yes.BOTTOM:
42829                 s.el.setY(s.resizingEl.getTop() - s.el.getHeight());
42830                 break;
42831         }
42832     }
42833 };
42834
42835 /**
42836  * Orientation constant - Create a vertical SplitBar
42837  * @static
42838  * @type Number
42839  */
42840 Roo.bootstrap.SplitBar.VERTICAL = 1;
42841
42842 /**
42843  * Orientation constant - Create a horizontal SplitBar
42844  * @static
42845  * @type Number
42846  */
42847 Roo.bootstrap.SplitBar.HORIZONTAL = 2;
42848
42849 /**
42850  * Placement constant - The resizing element is to the left of the splitter element
42851  * @static
42852  * @type Number
42853  */
42854 Roo.bootstrap.SplitBar.LEFT = 1;
42855
42856 /**
42857  * Placement constant - The resizing element is to the right of the splitter element
42858  * @static
42859  * @type Number
42860  */
42861 Roo.bootstrap.SplitBar.RIGHT = 2;
42862
42863 /**
42864  * Placement constant - The resizing element is positioned above the splitter element
42865  * @static
42866  * @type Number
42867  */
42868 Roo.bootstrap.SplitBar.TOP = 3;
42869
42870 /**
42871  * Placement constant - The resizing element is positioned under splitter element
42872  * @static
42873  * @type Number
42874  */
42875 Roo.bootstrap.SplitBar.BOTTOM = 4;
42876 /*
42877  * Based on:
42878  * Ext JS Library 1.1.1
42879  * Copyright(c) 2006-2007, Ext JS, LLC.
42880  *
42881  * Originally Released Under LGPL - original licence link has changed is not relivant.
42882  *
42883  * Fork - LGPL
42884  * <script type="text/javascript">
42885  */
42886
42887 /**
42888  * @class Roo.bootstrap.layout.Manager
42889  * @extends Roo.bootstrap.Component
42890  * @abstract
42891  * Base class for layout managers.
42892  */
42893 Roo.bootstrap.layout.Manager = function(config)
42894 {
42895     this.monitorWindowResize = true; // do this before we apply configuration.
42896     
42897     Roo.bootstrap.layout.Manager.superclass.constructor.call(this,config);
42898
42899
42900
42901
42902
42903     /** false to disable window resize monitoring @type Boolean */
42904     
42905     this.regions = {};
42906     this.addEvents({
42907         /**
42908          * @event layout
42909          * Fires when a layout is performed.
42910          * @param {Roo.LayoutManager} this
42911          */
42912         "layout" : true,
42913         /**
42914          * @event regionresized
42915          * Fires when the user resizes a region.
42916          * @param {Roo.LayoutRegion} region The resized region
42917          * @param {Number} newSize The new size (width for east/west, height for north/south)
42918          */
42919         "regionresized" : true,
42920         /**
42921          * @event regioncollapsed
42922          * Fires when a region is collapsed.
42923          * @param {Roo.LayoutRegion} region The collapsed region
42924          */
42925         "regioncollapsed" : true,
42926         /**
42927          * @event regionexpanded
42928          * Fires when a region is expanded.
42929          * @param {Roo.LayoutRegion} region The expanded region
42930          */
42931         "regionexpanded" : true
42932     });
42933     this.updating = false;
42934
42935     if (config.el) {
42936         this.el = Roo.get(config.el);
42937         this.initEvents();
42938     }
42939
42940 };
42941
42942 Roo.extend(Roo.bootstrap.layout.Manager, Roo.bootstrap.Component, {
42943
42944
42945     regions : null,
42946
42947     monitorWindowResize : true,
42948
42949
42950     updating : false,
42951
42952
42953     onRender : function(ct, position)
42954     {
42955         if(!this.el){
42956             this.el = Roo.get(ct);
42957             this.initEvents();
42958         }
42959         //this.fireEvent('render',this);
42960     },
42961
42962
42963     initEvents: function()
42964     {
42965
42966
42967         // ie scrollbar fix
42968         if(this.el.dom == document.body && Roo.isIE && !config.allowScroll){
42969             document.body.scroll = "no";
42970         }else if(this.el.dom != document.body && this.el.getStyle('position') == 'static'){
42971             this.el.position('relative');
42972         }
42973         this.id = this.el.id;
42974         this.el.addClass("roo-layout-container");
42975         Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
42976         if(this.el.dom != document.body ) {
42977             this.el.on('resize', this.layout,this);
42978             this.el.on('show', this.layout,this);
42979         }
42980
42981     },
42982
42983     /**
42984      * Returns true if this layout is currently being updated
42985      * @return {Boolean}
42986      */
42987     isUpdating : function(){
42988         return this.updating;
42989     },
42990
42991     /**
42992      * Suspend the LayoutManager from doing auto-layouts while
42993      * making multiple add or remove calls
42994      */
42995     beginUpdate : function(){
42996         this.updating = true;
42997     },
42998
42999     /**
43000      * Restore auto-layouts and optionally disable the manager from performing a layout
43001      * @param {Boolean} noLayout true to disable a layout update
43002      */
43003     endUpdate : function(noLayout){
43004         this.updating = false;
43005         if(!noLayout){
43006             this.layout();
43007         }
43008     },
43009
43010     layout: function(){
43011         // abstract...
43012     },
43013
43014     onRegionResized : function(region, newSize){
43015         this.fireEvent("regionresized", region, newSize);
43016         this.layout();
43017     },
43018
43019     onRegionCollapsed : function(region){
43020         this.fireEvent("regioncollapsed", region);
43021     },
43022
43023     onRegionExpanded : function(region){
43024         this.fireEvent("regionexpanded", region);
43025     },
43026
43027     /**
43028      * Returns the size of the current view. This method normalizes document.body and element embedded layouts and
43029      * performs box-model adjustments.
43030      * @return {Object} The size as an object {width: (the width), height: (the height)}
43031      */
43032     getViewSize : function()
43033     {
43034         var size;
43035         if(this.el.dom != document.body){
43036             size = this.el.getSize();
43037         }else{
43038             size = {width: Roo.lib.Dom.getViewWidth(), height: Roo.lib.Dom.getViewHeight()};
43039         }
43040         size.width -= this.el.getBorderWidth("lr")-this.el.getPadding("lr");
43041         size.height -= this.el.getBorderWidth("tb")-this.el.getPadding("tb");
43042         return size;
43043     },
43044
43045     /**
43046      * Returns the Element this layout is bound to.
43047      * @return {Roo.Element}
43048      */
43049     getEl : function(){
43050         return this.el;
43051     },
43052
43053     /**
43054      * Returns the specified region.
43055      * @param {String} target The region key ('center', 'north', 'south', 'east' or 'west')
43056      * @return {Roo.LayoutRegion}
43057      */
43058     getRegion : function(target){
43059         return this.regions[target.toLowerCase()];
43060     },
43061
43062     onWindowResize : function(){
43063         if(this.monitorWindowResize){
43064             this.layout();
43065         }
43066     }
43067 });
43068 /*
43069  * Based on:
43070  * Ext JS Library 1.1.1
43071  * Copyright(c) 2006-2007, Ext JS, LLC.
43072  *
43073  * Originally Released Under LGPL - original licence link has changed is not relivant.
43074  *
43075  * Fork - LGPL
43076  * <script type="text/javascript">
43077  */
43078 /**
43079  * @class Roo.bootstrap.layout.Border
43080  * @extends Roo.bootstrap.layout.Manager
43081  * @children Roo.bootstrap.panel.Content Roo.bootstrap.panel.Nest Roo.bootstrap.panel.Grid
43082  * @parent builder Roo.bootstrap.panel.Nest Roo.bootstrap.panel.Nest Roo.bootstrap.Modal
43083  * This class represents a common layout manager used in desktop applications. For screenshots and more details,
43084  * please see: examples/bootstrap/nested.html<br><br>
43085  
43086 <b>The container the layout is rendered into can be either the body element or any other element.
43087 If it is not the body element, the container needs to either be an absolute positioned element,
43088 or you will need to add "position:relative" to the css of the container.  You will also need to specify
43089 the container size if it is not the body element.</b>
43090
43091 * @constructor
43092 * Create a new Border
43093 * @param {Object} config Configuration options
43094  */
43095 Roo.bootstrap.layout.Border = function(config){
43096     config = config || {};
43097     Roo.bootstrap.layout.Border.superclass.constructor.call(this, config);
43098     
43099     
43100     
43101     Roo.each(Roo.bootstrap.layout.Border.regions, function(region) {
43102         if(config[region]){
43103             config[region].region = region;
43104             this.addRegion(config[region]);
43105         }
43106     },this);
43107     
43108 };
43109
43110 Roo.bootstrap.layout.Border.regions =  ["center", "north","south","east","west"];
43111
43112 Roo.extend(Roo.bootstrap.layout.Border, Roo.bootstrap.layout.Manager, {
43113     
43114         /**
43115          * @cfg {Roo.bootstrap.layout.Region} center region to go in center
43116          */
43117         /**
43118          * @cfg {Roo.bootstrap.layout.Region} west region to go in west
43119          */
43120         /**
43121          * @cfg {Roo.bootstrap.layout.Region} east region to go in east
43122          */
43123         /**
43124          * @cfg {Roo.bootstrap.layout.Region} south region to go in south
43125          */
43126         /**
43127          * @cfg {Roo.bootstrap.layout.Region} north region to go in north
43128          */
43129         
43130         
43131         
43132         
43133     parent : false, // this might point to a 'nest' or a ???
43134     
43135     /**
43136      * Creates and adds a new region if it doesn't already exist.
43137      * @param {String} target The target region key (north, south, east, west or center).
43138      * @param {Object} config The regions config object
43139      * @return {BorderLayoutRegion} The new region
43140      */
43141     addRegion : function(config)
43142     {
43143         if(!this.regions[config.region]){
43144             var r = this.factory(config);
43145             this.bindRegion(r);
43146         }
43147         return this.regions[config.region];
43148     },
43149
43150     // private (kinda)
43151     bindRegion : function(r){
43152         this.regions[r.config.region] = r;
43153         
43154         r.on("visibilitychange",    this.layout, this);
43155         r.on("paneladded",          this.layout, this);
43156         r.on("panelremoved",        this.layout, this);
43157         r.on("invalidated",         this.layout, this);
43158         r.on("resized",             this.onRegionResized, this);
43159         r.on("collapsed",           this.onRegionCollapsed, this);
43160         r.on("expanded",            this.onRegionExpanded, this);
43161     },
43162
43163     /**
43164      * Performs a layout update.
43165      */
43166     layout : function()
43167     {
43168         if(this.updating) {
43169             return;
43170         }
43171         
43172         // render all the rebions if they have not been done alreayd?
43173         Roo.each(Roo.bootstrap.layout.Border.regions, function(region) {
43174             if(this.regions[region] && !this.regions[region].bodyEl){
43175                 this.regions[region].onRender(this.el)
43176             }
43177         },this);
43178         
43179         var size = this.getViewSize();
43180         var w = size.width;
43181         var h = size.height;
43182         var centerW = w;
43183         var centerH = h;
43184         var centerY = 0;
43185         var centerX = 0;
43186         //var x = 0, y = 0;
43187
43188         var rs = this.regions;
43189         var north = rs["north"];
43190         var south = rs["south"]; 
43191         var west = rs["west"];
43192         var east = rs["east"];
43193         var center = rs["center"];
43194         //if(this.hideOnLayout){ // not supported anymore
43195             //c.el.setStyle("display", "none");
43196         //}
43197         if(north && north.isVisible()){
43198             var b = north.getBox();
43199             var m = north.getMargins();
43200             b.width = w - (m.left+m.right);
43201             b.x = m.left;
43202             b.y = m.top;
43203             centerY = b.height + b.y + m.bottom;
43204             centerH -= centerY;
43205             north.updateBox(this.safeBox(b));
43206         }
43207         if(south && south.isVisible()){
43208             var b = south.getBox();
43209             var m = south.getMargins();
43210             b.width = w - (m.left+m.right);
43211             b.x = m.left;
43212             var totalHeight = (b.height + m.top + m.bottom);
43213             b.y = h - totalHeight + m.top;
43214             centerH -= totalHeight;
43215             south.updateBox(this.safeBox(b));
43216         }
43217         if(west && west.isVisible()){
43218             var b = west.getBox();
43219             var m = west.getMargins();
43220             b.height = centerH - (m.top+m.bottom);
43221             b.x = m.left;
43222             b.y = centerY + m.top;
43223             var totalWidth = (b.width + m.left + m.right);
43224             centerX += totalWidth;
43225             centerW -= totalWidth;
43226             west.updateBox(this.safeBox(b));
43227         }
43228         if(east && east.isVisible()){
43229             var b = east.getBox();
43230             var m = east.getMargins();
43231             b.height = centerH - (m.top+m.bottom);
43232             var totalWidth = (b.width + m.left + m.right);
43233             b.x = w - totalWidth + m.left;
43234             b.y = centerY + m.top;
43235             centerW -= totalWidth;
43236             east.updateBox(this.safeBox(b));
43237         }
43238         if(center){
43239             var m = center.getMargins();
43240             var centerBox = {
43241                 x: centerX + m.left,
43242                 y: centerY + m.top,
43243                 width: centerW - (m.left+m.right),
43244                 height: centerH - (m.top+m.bottom)
43245             };
43246             //if(this.hideOnLayout){
43247                 //center.el.setStyle("display", "block");
43248             //}
43249             center.updateBox(this.safeBox(centerBox));
43250         }
43251         this.el.repaint();
43252         this.fireEvent("layout", this);
43253     },
43254
43255     // private
43256     safeBox : function(box){
43257         box.width = Math.max(0, box.width);
43258         box.height = Math.max(0, box.height);
43259         return box;
43260     },
43261
43262     /**
43263      * Adds a ContentPanel (or subclass) to this layout.
43264      * @param {String} target The target region key (north, south, east, west or center).
43265      * @param {Roo.ContentPanel} panel The panel to add
43266      * @return {Roo.ContentPanel} The added panel
43267      */
43268     add : function(target, panel){
43269          
43270         target = target.toLowerCase();
43271         return this.regions[target].add(panel);
43272     },
43273
43274     /**
43275      * Remove a ContentPanel (or subclass) to this layout.
43276      * @param {String} target The target region key (north, south, east, west or center).
43277      * @param {Number/String/Roo.ContentPanel} panel The index, id or panel to remove
43278      * @return {Roo.ContentPanel} The removed panel
43279      */
43280     remove : function(target, panel){
43281         target = target.toLowerCase();
43282         return this.regions[target].remove(panel);
43283     },
43284
43285     /**
43286      * Searches all regions for a panel with the specified id
43287      * @param {String} panelId
43288      * @return {Roo.ContentPanel} The panel or null if it wasn't found
43289      */
43290     findPanel : function(panelId){
43291         var rs = this.regions;
43292         for(var target in rs){
43293             if(typeof rs[target] != "function"){
43294                 var p = rs[target].getPanel(panelId);
43295                 if(p){
43296                     return p;
43297                 }
43298             }
43299         }
43300         return null;
43301     },
43302
43303     /**
43304      * Searches all regions for a panel with the specified id and activates (shows) it.
43305      * @param {String/ContentPanel} panelId The panels id or the panel itself
43306      * @return {Roo.ContentPanel} The shown panel or null
43307      */
43308     showPanel : function(panelId) {
43309       var rs = this.regions;
43310       for(var target in rs){
43311          var r = rs[target];
43312          if(typeof r != "function"){
43313             if(r.hasPanel(panelId)){
43314                return r.showPanel(panelId);
43315             }
43316          }
43317       }
43318       return null;
43319    },
43320
43321    /**
43322      * Restores this layout's state using Roo.state.Manager or the state provided by the passed provider.
43323      * @param {Roo.state.Provider} provider (optional) An alternate state provider
43324      */
43325    /*
43326     restoreState : function(provider){
43327         if(!provider){
43328             provider = Roo.state.Manager;
43329         }
43330         var sm = new Roo.LayoutStateManager();
43331         sm.init(this, provider);
43332     },
43333 */
43334  
43335  
43336     /**
43337      * Adds a xtype elements to the layout.
43338      * <pre><code>
43339
43340 layout.addxtype({
43341        xtype : 'ContentPanel',
43342        region: 'west',
43343        items: [ .... ]
43344    }
43345 );
43346
43347 layout.addxtype({
43348         xtype : 'NestedLayoutPanel',
43349         region: 'west',
43350         layout: {
43351            center: { },
43352            west: { }   
43353         },
43354         items : [ ... list of content panels or nested layout panels.. ]
43355    }
43356 );
43357 </code></pre>
43358      * @param {Object} cfg Xtype definition of item to add.
43359      */
43360     addxtype : function(cfg)
43361     {
43362         // basically accepts a pannel...
43363         // can accept a layout region..!?!?
43364         //Roo.log('Roo.BorderLayout add ' + cfg.xtype)
43365         
43366         
43367         // theory?  children can only be panels??
43368         
43369         //if (!cfg.xtype.match(/Panel$/)) {
43370         //    return false;
43371         //}
43372         var ret = false;
43373         
43374         if (typeof(cfg.region) == 'undefined') {
43375             Roo.log("Failed to add Panel, region was not set");
43376             Roo.log(cfg);
43377             return false;
43378         }
43379         var region = cfg.region;
43380         delete cfg.region;
43381         
43382           
43383         var xitems = [];
43384         if (cfg.items) {
43385             xitems = cfg.items;
43386             delete cfg.items;
43387         }
43388         var nb = false;
43389         
43390         if ( region == 'center') {
43391             Roo.log("Center: " + cfg.title);
43392         }
43393         
43394         
43395         switch(cfg.xtype) 
43396         {
43397             case 'Content':  // ContentPanel (el, cfg)
43398             case 'Scroll':  // ContentPanel (el, cfg)
43399             case 'View': 
43400                 cfg.autoCreate = cfg.autoCreate || true;
43401                 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
43402                 //} else {
43403                 //    var el = this.el.createChild();
43404                 //    ret = new Roo[cfg.xtype](el, cfg); // new panel!!!!!
43405                 //}
43406                 
43407                 this.add(region, ret);
43408                 break;
43409             
43410             /*
43411             case 'TreePanel': // our new panel!
43412                 cfg.el = this.el.createChild();
43413                 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
43414                 this.add(region, ret);
43415                 break;
43416             */
43417             
43418             case 'Nest': 
43419                 // create a new Layout (which is  a Border Layout...
43420                 
43421                 var clayout = cfg.layout;
43422                 clayout.el  = this.el.createChild();
43423                 clayout.items   = clayout.items  || [];
43424                 
43425                 delete cfg.layout;
43426                 
43427                 // replace this exitems with the clayout ones..
43428                 xitems = clayout.items;
43429                  
43430                 // force background off if it's in center...
43431                 if (region == 'center' && this.active && this.getRegion('center').panels.length < 1) {
43432                     cfg.background = false;
43433                 }
43434                 cfg.layout  = new Roo.bootstrap.layout.Border(clayout);
43435                 
43436                 
43437                 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
43438                 //console.log('adding nested layout panel '  + cfg.toSource());
43439                 this.add(region, ret);
43440                 nb = {}; /// find first...
43441                 break;
43442             
43443             case 'Grid':
43444                 
43445                 // needs grid and region
43446                 
43447                 //var el = this.getRegion(region).el.createChild();
43448                 /*
43449                  *var el = this.el.createChild();
43450                 // create the grid first...
43451                 cfg.grid.container = el;
43452                 cfg.grid = new cfg.grid.xns[cfg.grid.xtype](cfg.grid);
43453                 */
43454                 
43455                 if (region == 'center' && this.active ) {
43456                     cfg.background = false;
43457                 }
43458                 
43459                 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
43460                 
43461                 this.add(region, ret);
43462                 /*
43463                 if (cfg.background) {
43464                     // render grid on panel activation (if panel background)
43465                     ret.on('activate', function(gp) {
43466                         if (!gp.grid.rendered) {
43467                     //        gp.grid.render(el);
43468                         }
43469                     });
43470                 } else {
43471                   //  cfg.grid.render(el);
43472                 }
43473                 */
43474                 break;
43475            
43476            
43477             case 'Border': // it can get called on it'self... - might need to check if this is fixed?
43478                 // it was the old xcomponent building that caused this before.
43479                 // espeically if border is the top element in the tree.
43480                 ret = this;
43481                 break; 
43482                 
43483                     
43484                 
43485                 
43486                 
43487             default:
43488                 /*
43489                 if (typeof(Roo[cfg.xtype]) != 'undefined') {
43490                     
43491                     ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
43492                     this.add(region, ret);
43493                 } else {
43494                 */
43495                     Roo.log(cfg);
43496                     throw "Can not add '" + cfg.xtype + "' to Border";
43497                     return null;
43498              
43499                                 
43500              
43501         }
43502         this.beginUpdate();
43503         // add children..
43504         var region = '';
43505         var abn = {};
43506         Roo.each(xitems, function(i)  {
43507             region = nb && i.region ? i.region : false;
43508             
43509             var add = ret.addxtype(i);
43510            
43511             if (region) {
43512                 nb[region] = nb[region] == undefined ? 0 : nb[region]+1;
43513                 if (!i.background) {
43514                     abn[region] = nb[region] ;
43515                 }
43516             }
43517             
43518         });
43519         this.endUpdate();
43520
43521         // make the last non-background panel active..
43522         //if (nb) { Roo.log(abn); }
43523         if (nb) {
43524             
43525             for(var r in abn) {
43526                 region = this.getRegion(r);
43527                 if (region) {
43528                     // tried using nb[r], but it does not work..
43529                      
43530                     region.showPanel(abn[r]);
43531                    
43532                 }
43533             }
43534         }
43535         return ret;
43536         
43537     },
43538     
43539     
43540 // private
43541     factory : function(cfg)
43542     {
43543         
43544         var validRegions = Roo.bootstrap.layout.Border.regions;
43545
43546         var target = cfg.region;
43547         cfg.mgr = this;
43548         
43549         var r = Roo.bootstrap.layout;
43550         Roo.log(target);
43551         switch(target){
43552             case "north":
43553                 return new r.North(cfg);
43554             case "south":
43555                 return new r.South(cfg);
43556             case "east":
43557                 return new r.East(cfg);
43558             case "west":
43559                 return new r.West(cfg);
43560             case "center":
43561                 return new r.Center(cfg);
43562         }
43563         throw 'Layout region "'+target+'" not supported.';
43564     }
43565     
43566     
43567 });
43568  /*
43569  * Based on:
43570  * Ext JS Library 1.1.1
43571  * Copyright(c) 2006-2007, Ext JS, LLC.
43572  *
43573  * Originally Released Under LGPL - original licence link has changed is not relivant.
43574  *
43575  * Fork - LGPL
43576  * <script type="text/javascript">
43577  */
43578  
43579 /**
43580  * @class Roo.bootstrap.layout.Basic
43581  * @extends Roo.util.Observable
43582  * This class represents a lightweight region in a layout manager. This region does not move dom nodes
43583  * and does not have a titlebar, tabs or any other features. All it does is size and position 
43584  * panels. To create a BasicLayoutRegion, add lightweight:true or basic:true to your regions config.
43585  * @cfg {Roo.bootstrap.layout.Manager}   mgr The manager
43586  * @cfg {string}   region  the region that it inhabits..
43587  * @cfg {bool}   skipConfig skip config?
43588  * 
43589
43590  */
43591 Roo.bootstrap.layout.Basic = function(config){
43592     
43593     this.mgr = config.mgr;
43594     
43595     this.position = config.region;
43596     
43597     var skipConfig = config.skipConfig;
43598     
43599     this.events = {
43600         /**
43601          * @scope Roo.BasicLayoutRegion
43602          */
43603         
43604         /**
43605          * @event beforeremove
43606          * Fires before a panel is removed (or closed). To cancel the removal set "e.cancel = true" on the event argument.
43607          * @param {Roo.LayoutRegion} this
43608          * @param {Roo.ContentPanel} panel The panel
43609          * @param {Object} e The cancel event object
43610          */
43611         "beforeremove" : true,
43612         /**
43613          * @event invalidated
43614          * Fires when the layout for this region is changed.
43615          * @param {Roo.LayoutRegion} this
43616          */
43617         "invalidated" : true,
43618         /**
43619          * @event visibilitychange
43620          * Fires when this region is shown or hidden 
43621          * @param {Roo.LayoutRegion} this
43622          * @param {Boolean} visibility true or false
43623          */
43624         "visibilitychange" : true,
43625         /**
43626          * @event paneladded
43627          * Fires when a panel is added. 
43628          * @param {Roo.LayoutRegion} this
43629          * @param {Roo.ContentPanel} panel The panel
43630          */
43631         "paneladded" : true,
43632         /**
43633          * @event panelremoved
43634          * Fires when a panel is removed. 
43635          * @param {Roo.LayoutRegion} this
43636          * @param {Roo.ContentPanel} panel The panel
43637          */
43638         "panelremoved" : true,
43639         /**
43640          * @event beforecollapse
43641          * Fires when this region before collapse.
43642          * @param {Roo.LayoutRegion} this
43643          */
43644         "beforecollapse" : true,
43645         /**
43646          * @event collapsed
43647          * Fires when this region is collapsed.
43648          * @param {Roo.LayoutRegion} this
43649          */
43650         "collapsed" : true,
43651         /**
43652          * @event expanded
43653          * Fires when this region is expanded.
43654          * @param {Roo.LayoutRegion} this
43655          */
43656         "expanded" : true,
43657         /**
43658          * @event slideshow
43659          * Fires when this region is slid into view.
43660          * @param {Roo.LayoutRegion} this
43661          */
43662         "slideshow" : true,
43663         /**
43664          * @event slidehide
43665          * Fires when this region slides out of view. 
43666          * @param {Roo.LayoutRegion} this
43667          */
43668         "slidehide" : true,
43669         /**
43670          * @event panelactivated
43671          * Fires when a panel is activated. 
43672          * @param {Roo.LayoutRegion} this
43673          * @param {Roo.ContentPanel} panel The activated panel
43674          */
43675         "panelactivated" : true,
43676         /**
43677          * @event resized
43678          * Fires when the user resizes this region. 
43679          * @param {Roo.LayoutRegion} this
43680          * @param {Number} newSize The new size (width for east/west, height for north/south)
43681          */
43682         "resized" : true
43683     };
43684     /** A collection of panels in this region. @type Roo.util.MixedCollection */
43685     this.panels = new Roo.util.MixedCollection();
43686     this.panels.getKey = this.getPanelId.createDelegate(this);
43687     this.box = null;
43688     this.activePanel = null;
43689     // ensure listeners are added...
43690     
43691     if (config.listeners || config.events) {
43692         Roo.bootstrap.layout.Basic.superclass.constructor.call(this, {
43693             listeners : config.listeners || {},
43694             events : config.events || {}
43695         });
43696     }
43697     
43698     if(skipConfig !== true){
43699         this.applyConfig(config);
43700     }
43701 };
43702
43703 Roo.extend(Roo.bootstrap.layout.Basic, Roo.util.Observable,
43704 {
43705     getPanelId : function(p){
43706         return p.getId();
43707     },
43708     
43709     applyConfig : function(config){
43710         this.margins = config.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
43711         this.config = config;
43712         
43713     },
43714     
43715     /**
43716      * Resizes the region to the specified size. For vertical regions (west, east) this adjusts 
43717      * the width, for horizontal (north, south) the height.
43718      * @param {Number} newSize The new width or height
43719      */
43720     resizeTo : function(newSize){
43721         var el = this.el ? this.el :
43722                  (this.activePanel ? this.activePanel.getEl() : null);
43723         if(el){
43724             switch(this.position){
43725                 case "east":
43726                 case "west":
43727                     el.setWidth(newSize);
43728                     this.fireEvent("resized", this, newSize);
43729                 break;
43730                 case "north":
43731                 case "south":
43732                     el.setHeight(newSize);
43733                     this.fireEvent("resized", this, newSize);
43734                 break;                
43735             }
43736         }
43737     },
43738     
43739     getBox : function(){
43740         return this.activePanel ? this.activePanel.getEl().getBox(false, true) : null;
43741     },
43742     
43743     getMargins : function(){
43744         return this.margins;
43745     },
43746     
43747     updateBox : function(box){
43748         this.box = box;
43749         var el = this.activePanel.getEl();
43750         el.dom.style.left = box.x + "px";
43751         el.dom.style.top = box.y + "px";
43752         this.activePanel.setSize(box.width, box.height);
43753     },
43754     
43755     /**
43756      * Returns the container element for this region.
43757      * @return {Roo.Element}
43758      */
43759     getEl : function(){
43760         return this.activePanel;
43761     },
43762     
43763     /**
43764      * Returns true if this region is currently visible.
43765      * @return {Boolean}
43766      */
43767     isVisible : function(){
43768         return this.activePanel ? true : false;
43769     },
43770     
43771     setActivePanel : function(panel){
43772         panel = this.getPanel(panel);
43773         if(this.activePanel && this.activePanel != panel){
43774             this.activePanel.setActiveState(false);
43775             this.activePanel.getEl().setLeftTop(-10000,-10000);
43776         }
43777         this.activePanel = panel;
43778         panel.setActiveState(true);
43779         if(this.box){
43780             panel.setSize(this.box.width, this.box.height);
43781         }
43782         this.fireEvent("panelactivated", this, panel);
43783         this.fireEvent("invalidated");
43784     },
43785     
43786     /**
43787      * Show the specified panel.
43788      * @param {Number/String/ContentPanel} panelId The panels index, id or the panel itself
43789      * @return {Roo.ContentPanel} The shown panel or null
43790      */
43791     showPanel : function(panel){
43792         panel = this.getPanel(panel);
43793         if(panel){
43794             this.setActivePanel(panel);
43795         }
43796         return panel;
43797     },
43798     
43799     /**
43800      * Get the active panel for this region.
43801      * @return {Roo.ContentPanel} The active panel or null
43802      */
43803     getActivePanel : function(){
43804         return this.activePanel;
43805     },
43806     
43807     /**
43808      * Add the passed ContentPanel(s)
43809      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
43810      * @return {Roo.ContentPanel} The panel added (if only one was added)
43811      */
43812     add : function(panel){
43813         if(arguments.length > 1){
43814             for(var i = 0, len = arguments.length; i < len; i++) {
43815                 this.add(arguments[i]);
43816             }
43817             return null;
43818         }
43819         if(this.hasPanel(panel)){
43820             this.showPanel(panel);
43821             return panel;
43822         }
43823         var el = panel.getEl();
43824         if(el.dom.parentNode != this.mgr.el.dom){
43825             this.mgr.el.dom.appendChild(el.dom);
43826         }
43827         if(panel.setRegion){
43828             panel.setRegion(this);
43829         }
43830         this.panels.add(panel);
43831         el.setStyle("position", "absolute");
43832         if(!panel.background){
43833             this.setActivePanel(panel);
43834             if(this.config.initialSize && this.panels.getCount()==1){
43835                 this.resizeTo(this.config.initialSize);
43836             }
43837         }
43838         this.fireEvent("paneladded", this, panel);
43839         return panel;
43840     },
43841     
43842     /**
43843      * Returns true if the panel is in this region.
43844      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
43845      * @return {Boolean}
43846      */
43847     hasPanel : function(panel){
43848         if(typeof panel == "object"){ // must be panel obj
43849             panel = panel.getId();
43850         }
43851         return this.getPanel(panel) ? true : false;
43852     },
43853     
43854     /**
43855      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
43856      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
43857      * @param {Boolean} preservePanel Overrides the config preservePanel option
43858      * @return {Roo.ContentPanel} The panel that was removed
43859      */
43860     remove : function(panel, preservePanel){
43861         panel = this.getPanel(panel);
43862         if(!panel){
43863             return null;
43864         }
43865         var e = {};
43866         this.fireEvent("beforeremove", this, panel, e);
43867         if(e.cancel === true){
43868             return null;
43869         }
43870         var panelId = panel.getId();
43871         this.panels.removeKey(panelId);
43872         return panel;
43873     },
43874     
43875     /**
43876      * Returns the panel specified or null if it's not in this region.
43877      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
43878      * @return {Roo.ContentPanel}
43879      */
43880     getPanel : function(id){
43881         if(typeof id == "object"){ // must be panel obj
43882             return id;
43883         }
43884         return this.panels.get(id);
43885     },
43886     
43887     /**
43888      * Returns this regions position (north/south/east/west/center).
43889      * @return {String} 
43890      */
43891     getPosition: function(){
43892         return this.position;    
43893     }
43894 });/*
43895  * Based on:
43896  * Ext JS Library 1.1.1
43897  * Copyright(c) 2006-2007, Ext JS, LLC.
43898  *
43899  * Originally Released Under LGPL - original licence link has changed is not relivant.
43900  *
43901  * Fork - LGPL
43902  * <script type="text/javascript">
43903  */
43904  
43905 /**
43906  * @class Roo.bootstrap.layout.Region
43907  * @extends Roo.bootstrap.layout.Basic
43908  * This class represents a region in a layout manager.
43909  
43910  * @cfg {Object}    margins         Margins for the element (defaults to {top: 0, left: 0, right:0, bottom: 0})
43911  * @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})
43912  * @cfg {String}    tabPosition     (top|bottom) "top" or "bottom" (defaults to "bottom")
43913  * @cfg {Boolean}   alwaysShowTabs  True to always display tabs even when there is only 1 panel (defaults to false)
43914  * @cfg {Boolean}   autoScroll      True to enable overflow scrolling (defaults to false)
43915  * @cfg {Boolean}   titlebar        True to display a title bar (defaults to true)
43916  * @cfg {String}    title           The title for the region (overrides panel titles)
43917  * @cfg {Boolean}   animate         True to animate expand/collapse (defaults to false)
43918  * @cfg {Boolean}   autoHide        False to disable auto hiding when the mouse leaves the "floated" region (defaults to true)
43919  * @cfg {Boolean}   preservePanels  True to preserve removed panels so they can be readded later (defaults to false)
43920  * @cfg {Boolean}   closeOnTab      True to place the close icon on the tabs instead of the region titlebar (defaults to false)
43921  * @cfg {Boolean}   hideTabs        True to hide the tab strip (defaults to false)
43922  * @cfg {Boolean}   resizeTabs      True to enable automatic tab resizing. This will resize the tabs so they are all the same size and fit within
43923  *                      the space available, similar to FireFox 1.5 tabs (defaults to false)
43924  * @cfg {Number}    minTabWidth     The minimum tab width (defaults to 40)
43925  * @cfg {Number}    preferredTabWidth The preferred tab width (defaults to 150)
43926  * @cfg {String}    overflow       (hidden|visible) if you have menus in the region, then you need to set this to visible.
43927
43928  * @cfg {Boolean}   hidden          True to start the region hidden (defaults to false)
43929  * @cfg {Boolean}   hideWhenEmpty   True to hide the region when it has no panels
43930  * @cfg {Boolean}   disableTabTips  True to disable tab tooltips
43931  * @cfg {Number}    width           For East/West panels
43932  * @cfg {Number}    height          For North/South panels
43933  * @cfg {Boolean}   split           To show the splitter
43934  * @cfg {Boolean}   toolbar         xtype configuration for a toolbar - shows on right of tabbar
43935  * 
43936  * @cfg {string}   cls             Extra CSS classes to add to region
43937  * 
43938  * @cfg {Roo.bootstrap.layout.Manager}   mgr The manager
43939  * @cfg {string}   region  the region that it inhabits..
43940  *
43941
43942  * @xxxcfg {Boolean}   collapsible     DISABLED False to disable collapsing (defaults to true)
43943  * @xxxcfg {Boolean}   collapsed       DISABLED True to set the initial display to collapsed (defaults to false)
43944
43945  * @xxxcfg {String}    collapsedTitle  DISABLED Optional string message to display in the collapsed block of a north or south region
43946  * @xxxxcfg {Boolean}   floatable       DISABLED False to disable floating (defaults to true)
43947  * @xxxxcfg {Boolean}   showPin         True to show a pin button NOT SUPPORTED YET
43948  */
43949 Roo.bootstrap.layout.Region = function(config)
43950 {
43951     this.applyConfig(config);
43952
43953     var mgr = config.mgr;
43954     var pos = config.region;
43955     config.skipConfig = true;
43956     Roo.bootstrap.layout.Region.superclass.constructor.call(this, config);
43957     
43958     if (mgr.el) {
43959         this.onRender(mgr.el);   
43960     }
43961      
43962     this.visible = true;
43963     this.collapsed = false;
43964     this.unrendered_panels = [];
43965 };
43966
43967 Roo.extend(Roo.bootstrap.layout.Region, Roo.bootstrap.layout.Basic, {
43968
43969     position: '', // set by wrapper (eg. north/south etc..)
43970     unrendered_panels : null,  // unrendered panels.
43971     
43972     tabPosition : false,
43973     
43974     mgr: false, // points to 'Border'
43975     
43976     
43977     createBody : function(){
43978         /** This region's body element 
43979         * @type Roo.Element */
43980         this.bodyEl = this.el.createChild({
43981                 tag: "div",
43982                 cls: "roo-layout-panel-body tab-content" // bootstrap added...
43983         });
43984     },
43985
43986     onRender: function(ctr, pos)
43987     {
43988         var dh = Roo.DomHelper;
43989         /** This region's container element 
43990         * @type Roo.Element */
43991         this.el = dh.append(ctr.dom, {
43992                 tag: "div",
43993                 cls: (this.config.cls || '') + " roo-layout-region roo-layout-panel roo-layout-panel-" + this.position
43994             }, true);
43995         /** This region's title element 
43996         * @type Roo.Element */
43997     
43998         this.titleEl = dh.append(this.el.dom,  {
43999                 tag: "div",
44000                 unselectable: "on",
44001                 cls: "roo-unselectable roo-layout-panel-hd breadcrumb roo-layout-title-" + this.position,
44002                 children:[
44003                     {tag: "span", cls: "roo-unselectable roo-layout-panel-hd-text", unselectable: "on", html: "&#160;"},
44004                     {tag: "div", cls: "roo-unselectable roo-layout-panel-hd-tools", unselectable: "on"}
44005                 ]
44006             }, true);
44007         
44008         this.titleEl.enableDisplayMode();
44009         /** This region's title text element 
44010         * @type HTMLElement */
44011         this.titleTextEl = this.titleEl.dom.firstChild;
44012         this.tools = Roo.get(this.titleEl.dom.childNodes[1], true);
44013         /*
44014         this.closeBtn = this.createTool(this.tools.dom, "roo-layout-close");
44015         this.closeBtn.enableDisplayMode();
44016         this.closeBtn.on("click", this.closeClicked, this);
44017         this.closeBtn.hide();
44018     */
44019         this.createBody(this.config);
44020         if(this.config.hideWhenEmpty){
44021             this.hide();
44022             this.on("paneladded", this.validateVisibility, this);
44023             this.on("panelremoved", this.validateVisibility, this);
44024         }
44025         if(this.autoScroll){
44026             this.bodyEl.setStyle("overflow", "auto");
44027         }else{
44028             this.bodyEl.setStyle("overflow", this.config.overflow || 'hidden');
44029         }
44030         //if(c.titlebar !== false){
44031             if((!this.config.titlebar && !this.config.title) || this.config.titlebar === false){
44032                 this.titleEl.hide();
44033             }else{
44034                 this.titleEl.show();
44035                 if(this.config.title){
44036                     this.titleTextEl.innerHTML = this.config.title;
44037                 }
44038             }
44039         //}
44040         if(this.config.collapsed){
44041             this.collapse(true);
44042         }
44043         if(this.config.hidden){
44044             this.hide();
44045         }
44046         
44047         if (this.unrendered_panels && this.unrendered_panels.length) {
44048             for (var i =0;i< this.unrendered_panels.length; i++) {
44049                 this.add(this.unrendered_panels[i]);
44050             }
44051             this.unrendered_panels = null;
44052             
44053         }
44054         
44055     },
44056     
44057     applyConfig : function(c)
44058     {
44059         /*
44060          *if(c.collapsible && this.position != "center" && !this.collapsedEl){
44061             var dh = Roo.DomHelper;
44062             if(c.titlebar !== false){
44063                 this.collapseBtn = this.createTool(this.tools.dom, "roo-layout-collapse-"+this.position);
44064                 this.collapseBtn.on("click", this.collapse, this);
44065                 this.collapseBtn.enableDisplayMode();
44066                 /*
44067                 if(c.showPin === true || this.showPin){
44068                     this.stickBtn = this.createTool(this.tools.dom, "roo-layout-stick");
44069                     this.stickBtn.enableDisplayMode();
44070                     this.stickBtn.on("click", this.expand, this);
44071                     this.stickBtn.hide();
44072                 }
44073                 
44074             }
44075             */
44076             /** This region's collapsed element
44077             * @type Roo.Element */
44078             /*
44079              *
44080             this.collapsedEl = dh.append(this.mgr.el.dom, {cls: "x-layout-collapsed x-layout-collapsed-"+this.position, children:[
44081                 {cls: "x-layout-collapsed-tools", children:[{cls: "x-layout-ctools-inner"}]}
44082             ]}, true);
44083             
44084             if(c.floatable !== false){
44085                this.collapsedEl.addClassOnOver("x-layout-collapsed-over");
44086                this.collapsedEl.on("click", this.collapseClick, this);
44087             }
44088
44089             if(c.collapsedTitle && (this.position == "north" || this.position== "south")) {
44090                 this.collapsedTitleTextEl = dh.append(this.collapsedEl.dom, {tag: "div", cls: "x-unselectable x-layout-panel-hd-text",
44091                    id: "message", unselectable: "on", style:{"float":"left"}});
44092                this.collapsedTitleTextEl.innerHTML = c.collapsedTitle;
44093              }
44094             this.expandBtn = this.createTool(this.collapsedEl.dom.firstChild.firstChild, "x-layout-expand-"+this.position);
44095             this.expandBtn.on("click", this.expand, this);
44096             
44097         }
44098         
44099         if(this.collapseBtn){
44100             this.collapseBtn.setVisible(c.collapsible == true);
44101         }
44102         
44103         this.cmargins = c.cmargins || this.cmargins ||
44104                          (this.position == "west" || this.position == "east" ?
44105                              {top: 0, left: 2, right:2, bottom: 0} :
44106                              {top: 2, left: 0, right:0, bottom: 2});
44107         */
44108         this.margins = c.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
44109         
44110         
44111         this.tabPosition = [ 'top','bottom', 'west'].indexOf(c.tabPosition) > -1 ? c.tabPosition : "top";
44112         
44113         this.autoScroll = c.autoScroll || false;
44114         
44115         
44116        
44117         
44118         this.duration = c.duration || .30;
44119         this.slideDuration = c.slideDuration || .45;
44120         this.config = c;
44121        
44122     },
44123     /**
44124      * Returns true if this region is currently visible.
44125      * @return {Boolean}
44126      */
44127     isVisible : function(){
44128         return this.visible;
44129     },
44130
44131     /**
44132      * Updates the title for collapsed north/south regions (used with {@link #collapsedTitle} config option)
44133      * @param {String} title (optional) The title text (accepts HTML markup, defaults to the numeric character reference for a non-breaking space, "&amp;#160;")
44134      */
44135     //setCollapsedTitle : function(title){
44136     //    title = title || "&#160;";
44137      //   if(this.collapsedTitleTextEl){
44138       //      this.collapsedTitleTextEl.innerHTML = title;
44139        // }
44140     //},
44141
44142     getBox : function(){
44143         var b;
44144       //  if(!this.collapsed){
44145             b = this.el.getBox(false, true);
44146        // }else{
44147           //  b = this.collapsedEl.getBox(false, true);
44148         //}
44149         return b;
44150     },
44151
44152     getMargins : function(){
44153         return this.margins;
44154         //return this.collapsed ? this.cmargins : this.margins;
44155     },
44156 /*
44157     highlight : function(){
44158         this.el.addClass("x-layout-panel-dragover");
44159     },
44160
44161     unhighlight : function(){
44162         this.el.removeClass("x-layout-panel-dragover");
44163     },
44164 */
44165     updateBox : function(box)
44166     {
44167         if (!this.bodyEl) {
44168             return; // not rendered yet..
44169         }
44170         
44171         this.box = box;
44172         if(!this.collapsed){
44173             this.el.dom.style.left = box.x + "px";
44174             this.el.dom.style.top = box.y + "px";
44175             this.updateBody(box.width, box.height);
44176         }else{
44177             this.collapsedEl.dom.style.left = box.x + "px";
44178             this.collapsedEl.dom.style.top = box.y + "px";
44179             this.collapsedEl.setSize(box.width, box.height);
44180         }
44181         if(this.tabs){
44182             this.tabs.autoSizeTabs();
44183         }
44184     },
44185
44186     updateBody : function(w, h)
44187     {
44188         if(w !== null){
44189             this.el.setWidth(w);
44190             w -= this.el.getBorderWidth("rl");
44191             if(this.config.adjustments){
44192                 w += this.config.adjustments[0];
44193             }
44194         }
44195         if(h !== null && h > 0){
44196             this.el.setHeight(h);
44197             h = this.titleEl && this.titleEl.isDisplayed() ? h - (this.titleEl.getHeight()||0) : h;
44198             h -= this.el.getBorderWidth("tb");
44199             if(this.config.adjustments){
44200                 h += this.config.adjustments[1];
44201             }
44202             this.bodyEl.setHeight(h);
44203             if(this.tabs){
44204                 h = this.tabs.syncHeight(h);
44205             }
44206         }
44207         if(this.panelSize){
44208             w = w !== null ? w : this.panelSize.width;
44209             h = h !== null ? h : this.panelSize.height;
44210         }
44211         if(this.activePanel){
44212             var el = this.activePanel.getEl();
44213             w = w !== null ? w : el.getWidth();
44214             h = h !== null ? h : el.getHeight();
44215             this.panelSize = {width: w, height: h};
44216             this.activePanel.setSize(w, h);
44217         }
44218         if(Roo.isIE && this.tabs){
44219             this.tabs.el.repaint();
44220         }
44221     },
44222
44223     /**
44224      * Returns the container element for this region.
44225      * @return {Roo.Element}
44226      */
44227     getEl : function(){
44228         return this.el;
44229     },
44230
44231     /**
44232      * Hides this region.
44233      */
44234     hide : function(){
44235         //if(!this.collapsed){
44236             this.el.dom.style.left = "-2000px";
44237             this.el.hide();
44238         //}else{
44239          //   this.collapsedEl.dom.style.left = "-2000px";
44240          //   this.collapsedEl.hide();
44241        // }
44242         this.visible = false;
44243         this.fireEvent("visibilitychange", this, false);
44244     },
44245
44246     /**
44247      * Shows this region if it was previously hidden.
44248      */
44249     show : function(){
44250         //if(!this.collapsed){
44251             this.el.show();
44252         //}else{
44253         //    this.collapsedEl.show();
44254        // }
44255         this.visible = true;
44256         this.fireEvent("visibilitychange", this, true);
44257     },
44258 /*
44259     closeClicked : function(){
44260         if(this.activePanel){
44261             this.remove(this.activePanel);
44262         }
44263     },
44264
44265     collapseClick : function(e){
44266         if(this.isSlid){
44267            e.stopPropagation();
44268            this.slideIn();
44269         }else{
44270            e.stopPropagation();
44271            this.slideOut();
44272         }
44273     },
44274 */
44275     /**
44276      * Collapses this region.
44277      * @param {Boolean} skipAnim (optional) true to collapse the element without animation (if animate is true)
44278      */
44279     /*
44280     collapse : function(skipAnim, skipCheck = false){
44281         if(this.collapsed) {
44282             return;
44283         }
44284         
44285         if(skipCheck || this.fireEvent("beforecollapse", this) != false){
44286             
44287             this.collapsed = true;
44288             if(this.split){
44289                 this.split.el.hide();
44290             }
44291             if(this.config.animate && skipAnim !== true){
44292                 this.fireEvent("invalidated", this);
44293                 this.animateCollapse();
44294             }else{
44295                 this.el.setLocation(-20000,-20000);
44296                 this.el.hide();
44297                 this.collapsedEl.show();
44298                 this.fireEvent("collapsed", this);
44299                 this.fireEvent("invalidated", this);
44300             }
44301         }
44302         
44303     },
44304 */
44305     animateCollapse : function(){
44306         // overridden
44307     },
44308
44309     /**
44310      * Expands this region if it was previously collapsed.
44311      * @param {Roo.EventObject} e The event that triggered the expand (or null if calling manually)
44312      * @param {Boolean} skipAnim (optional) true to expand the element without animation (if animate is true)
44313      */
44314     /*
44315     expand : function(e, skipAnim){
44316         if(e) {
44317             e.stopPropagation();
44318         }
44319         if(!this.collapsed || this.el.hasActiveFx()) {
44320             return;
44321         }
44322         if(this.isSlid){
44323             this.afterSlideIn();
44324             skipAnim = true;
44325         }
44326         this.collapsed = false;
44327         if(this.config.animate && skipAnim !== true){
44328             this.animateExpand();
44329         }else{
44330             this.el.show();
44331             if(this.split){
44332                 this.split.el.show();
44333             }
44334             this.collapsedEl.setLocation(-2000,-2000);
44335             this.collapsedEl.hide();
44336             this.fireEvent("invalidated", this);
44337             this.fireEvent("expanded", this);
44338         }
44339     },
44340 */
44341     animateExpand : function(){
44342         // overridden
44343     },
44344
44345     initTabs : function()
44346     {
44347         //this.bodyEl.setStyle("overflow", "hidden"); -- this is set in render?
44348         
44349         var ts = new Roo.bootstrap.panel.Tabs({
44350             el: this.bodyEl.dom,
44351             region : this,
44352             tabPosition: this.tabPosition ? this.tabPosition  : 'top',
44353             disableTooltips: this.config.disableTabTips,
44354             toolbar : this.config.toolbar
44355         });
44356         
44357         if(this.config.hideTabs){
44358             ts.stripWrap.setDisplayed(false);
44359         }
44360         this.tabs = ts;
44361         ts.resizeTabs = this.config.resizeTabs === true;
44362         ts.minTabWidth = this.config.minTabWidth || 40;
44363         ts.maxTabWidth = this.config.maxTabWidth || 250;
44364         ts.preferredTabWidth = this.config.preferredTabWidth || 150;
44365         ts.monitorResize = false;
44366         //ts.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden"); // this is set in render?
44367         ts.bodyEl.addClass('roo-layout-tabs-body');
44368         this.panels.each(this.initPanelAsTab, this);
44369     },
44370
44371     initPanelAsTab : function(panel){
44372         var ti = this.tabs.addTab(
44373             panel.getEl().id,
44374             panel.getTitle(),
44375             null,
44376             this.config.closeOnTab && panel.isClosable(),
44377             panel.tpl
44378         );
44379         if(panel.tabTip !== undefined){
44380             ti.setTooltip(panel.tabTip);
44381         }
44382         ti.on("activate", function(){
44383               this.setActivePanel(panel);
44384         }, this);
44385         
44386         if(this.config.closeOnTab){
44387             ti.on("beforeclose", function(t, e){
44388                 e.cancel = true;
44389                 this.remove(panel);
44390             }, this);
44391         }
44392         
44393         panel.tabItem = ti;
44394         
44395         return ti;
44396     },
44397
44398     updatePanelTitle : function(panel, title)
44399     {
44400         if(this.activePanel == panel){
44401             this.updateTitle(title);
44402         }
44403         if(this.tabs){
44404             var ti = this.tabs.getTab(panel.getEl().id);
44405             ti.setText(title);
44406             if(panel.tabTip !== undefined){
44407                 ti.setTooltip(panel.tabTip);
44408             }
44409         }
44410     },
44411
44412     updateTitle : function(title){
44413         if(this.titleTextEl && !this.config.title){
44414             this.titleTextEl.innerHTML = (typeof title != "undefined" && title.length > 0 ? title : "&#160;");
44415         }
44416     },
44417
44418     setActivePanel : function(panel)
44419     {
44420         panel = this.getPanel(panel);
44421         if(this.activePanel && this.activePanel != panel){
44422             if(this.activePanel.setActiveState(false) === false){
44423                 return;
44424             }
44425         }
44426         this.activePanel = panel;
44427         panel.setActiveState(true);
44428         if(this.panelSize){
44429             panel.setSize(this.panelSize.width, this.panelSize.height);
44430         }
44431         if(this.closeBtn){
44432             this.closeBtn.setVisible(!this.config.closeOnTab && !this.isSlid && panel.isClosable());
44433         }
44434         this.updateTitle(panel.getTitle());
44435         if(this.tabs){
44436             this.fireEvent("invalidated", this);
44437         }
44438         this.fireEvent("panelactivated", this, panel);
44439     },
44440
44441     /**
44442      * Shows the specified panel.
44443      * @param {Number/String/ContentPanel} panelId The panel's index, id or the panel itself
44444      * @return {Roo.ContentPanel} The shown panel, or null if a panel could not be found from panelId
44445      */
44446     showPanel : function(panel)
44447     {
44448         panel = this.getPanel(panel);
44449         if(panel){
44450             if(this.tabs){
44451                 var tab = this.tabs.getTab(panel.getEl().id);
44452                 if(tab.isHidden()){
44453                     this.tabs.unhideTab(tab.id);
44454                 }
44455                 tab.activate();
44456             }else{
44457                 this.setActivePanel(panel);
44458             }
44459         }
44460         return panel;
44461     },
44462
44463     /**
44464      * Get the active panel for this region.
44465      * @return {Roo.ContentPanel} The active panel or null
44466      */
44467     getActivePanel : function(){
44468         return this.activePanel;
44469     },
44470
44471     validateVisibility : function(){
44472         if(this.panels.getCount() < 1){
44473             this.updateTitle("&#160;");
44474             this.closeBtn.hide();
44475             this.hide();
44476         }else{
44477             if(!this.isVisible()){
44478                 this.show();
44479             }
44480         }
44481     },
44482
44483     /**
44484      * Adds the passed ContentPanel(s) to this region.
44485      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
44486      * @return {Roo.ContentPanel} The panel added (if only one was added; null otherwise)
44487      */
44488     add : function(panel)
44489     {
44490         if(arguments.length > 1){
44491             for(var i = 0, len = arguments.length; i < len; i++) {
44492                 this.add(arguments[i]);
44493             }
44494             return null;
44495         }
44496         
44497         // if we have not been rendered yet, then we can not really do much of this..
44498         if (!this.bodyEl) {
44499             this.unrendered_panels.push(panel);
44500             return panel;
44501         }
44502         
44503         
44504         
44505         
44506         if(this.hasPanel(panel)){
44507             this.showPanel(panel);
44508             return panel;
44509         }
44510         panel.setRegion(this);
44511         this.panels.add(panel);
44512        /* if(this.panels.getCount() == 1 && !this.config.alwaysShowTabs){
44513             // sinle panel - no tab...?? would it not be better to render it with the tabs,
44514             // and hide them... ???
44515             this.bodyEl.dom.appendChild(panel.getEl().dom);
44516             if(panel.background !== true){
44517                 this.setActivePanel(panel);
44518             }
44519             this.fireEvent("paneladded", this, panel);
44520             return panel;
44521         }
44522         */
44523         if(!this.tabs){
44524             this.initTabs();
44525         }else{
44526             this.initPanelAsTab(panel);
44527         }
44528         
44529         
44530         if(panel.background !== true){
44531             this.tabs.activate(panel.getEl().id);
44532         }
44533         this.fireEvent("paneladded", this, panel);
44534         return panel;
44535     },
44536
44537     /**
44538      * Hides the tab for the specified panel.
44539      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
44540      */
44541     hidePanel : function(panel){
44542         if(this.tabs && (panel = this.getPanel(panel))){
44543             this.tabs.hideTab(panel.getEl().id);
44544         }
44545     },
44546
44547     /**
44548      * Unhides the tab for a previously hidden panel.
44549      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
44550      */
44551     unhidePanel : function(panel){
44552         if(this.tabs && (panel = this.getPanel(panel))){
44553             this.tabs.unhideTab(panel.getEl().id);
44554         }
44555     },
44556
44557     clearPanels : function(){
44558         while(this.panels.getCount() > 0){
44559              this.remove(this.panels.first());
44560         }
44561     },
44562
44563     /**
44564      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
44565      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
44566      * @param {Boolean} preservePanel Overrides the config preservePanel option
44567      * @return {Roo.ContentPanel} The panel that was removed
44568      */
44569     remove : function(panel, preservePanel)
44570     {
44571         panel = this.getPanel(panel);
44572         if(!panel){
44573             return null;
44574         }
44575         var e = {};
44576         this.fireEvent("beforeremove", this, panel, e);
44577         if(e.cancel === true){
44578             return null;
44579         }
44580         preservePanel = (typeof preservePanel != "undefined" ? preservePanel : (this.config.preservePanels === true || panel.preserve === true));
44581         var panelId = panel.getId();
44582         this.panels.removeKey(panelId);
44583         if(preservePanel){
44584             document.body.appendChild(panel.getEl().dom);
44585         }
44586         if(this.tabs){
44587             this.tabs.removeTab(panel.getEl().id);
44588         }else if (!preservePanel){
44589             this.bodyEl.dom.removeChild(panel.getEl().dom);
44590         }
44591         if(this.panels.getCount() == 1 && this.tabs && !this.config.alwaysShowTabs){
44592             var p = this.panels.first();
44593             var tempEl = document.createElement("div"); // temp holder to keep IE from deleting the node
44594             tempEl.appendChild(p.getEl().dom);
44595             this.bodyEl.update("");
44596             this.bodyEl.dom.appendChild(p.getEl().dom);
44597             tempEl = null;
44598             this.updateTitle(p.getTitle());
44599             this.tabs = null;
44600             this.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
44601             this.setActivePanel(p);
44602         }
44603         panel.setRegion(null);
44604         if(this.activePanel == panel){
44605             this.activePanel = null;
44606         }
44607         if(this.config.autoDestroy !== false && preservePanel !== true){
44608             try{panel.destroy();}catch(e){}
44609         }
44610         this.fireEvent("panelremoved", this, panel);
44611         return panel;
44612     },
44613
44614     /**
44615      * Returns the TabPanel component used by this region
44616      * @return {Roo.TabPanel}
44617      */
44618     getTabs : function(){
44619         return this.tabs;
44620     },
44621
44622     createTool : function(parentEl, className){
44623         var btn = Roo.DomHelper.append(parentEl, {
44624             tag: "div",
44625             cls: "x-layout-tools-button",
44626             children: [ {
44627                 tag: "div",
44628                 cls: "roo-layout-tools-button-inner " + className,
44629                 html: "&#160;"
44630             }]
44631         }, true);
44632         btn.addClassOnOver("roo-layout-tools-button-over");
44633         return btn;
44634     }
44635 });/*
44636  * Based on:
44637  * Ext JS Library 1.1.1
44638  * Copyright(c) 2006-2007, Ext JS, LLC.
44639  *
44640  * Originally Released Under LGPL - original licence link has changed is not relivant.
44641  *
44642  * Fork - LGPL
44643  * <script type="text/javascript">
44644  */
44645  
44646
44647
44648 /**
44649  * @class Roo.SplitLayoutRegion
44650  * @extends Roo.LayoutRegion
44651  * Adds a splitbar and other (private) useful functionality to a {@link Roo.LayoutRegion}.
44652  */
44653 Roo.bootstrap.layout.Split = function(config){
44654     this.cursor = config.cursor;
44655     Roo.bootstrap.layout.Split.superclass.constructor.call(this, config);
44656 };
44657
44658 Roo.extend(Roo.bootstrap.layout.Split, Roo.bootstrap.layout.Region,
44659 {
44660     splitTip : "Drag to resize.",
44661     collapsibleSplitTip : "Drag to resize. Double click to hide.",
44662     useSplitTips : false,
44663
44664     applyConfig : function(config){
44665         Roo.bootstrap.layout.Split.superclass.applyConfig.call(this, config);
44666     },
44667     
44668     onRender : function(ctr,pos) {
44669         
44670         Roo.bootstrap.layout.Split.superclass.onRender.call(this, ctr,pos);
44671         if(!this.config.split){
44672             return;
44673         }
44674         if(!this.split){
44675             
44676             var splitEl = Roo.DomHelper.append(ctr.dom,  {
44677                             tag: "div",
44678                             id: this.el.id + "-split",
44679                             cls: "roo-layout-split roo-layout-split-"+this.position,
44680                             html: "&#160;"
44681             });
44682             /** The SplitBar for this region 
44683             * @type Roo.SplitBar */
44684             // does not exist yet...
44685             Roo.log([this.position, this.orientation]);
44686             
44687             this.split = new Roo.bootstrap.SplitBar({
44688                 dragElement : splitEl,
44689                 resizingElement: this.el,
44690                 orientation : this.orientation
44691             });
44692             
44693             this.split.on("moved", this.onSplitMove, this);
44694             this.split.useShim = this.config.useShim === true;
44695             this.split.getMaximumSize = this[this.position == 'north' || this.position == 'south' ? 'getVMaxSize' : 'getHMaxSize'].createDelegate(this);
44696             if(this.useSplitTips){
44697                 this.split.el.dom.title = this.config.collapsible ? this.collapsibleSplitTip : this.splitTip;
44698             }
44699             //if(config.collapsible){
44700             //    this.split.el.on("dblclick", this.collapse,  this);
44701             //}
44702         }
44703         if(typeof this.config.minSize != "undefined"){
44704             this.split.minSize = this.config.minSize;
44705         }
44706         if(typeof this.config.maxSize != "undefined"){
44707             this.split.maxSize = this.config.maxSize;
44708         }
44709         if(this.config.hideWhenEmpty || this.config.hidden || this.config.collapsed){
44710             this.hideSplitter();
44711         }
44712         
44713     },
44714
44715     getHMaxSize : function(){
44716          var cmax = this.config.maxSize || 10000;
44717          var center = this.mgr.getRegion("center");
44718          return Math.min(cmax, (this.el.getWidth()+center.getEl().getWidth())-center.getMinWidth());
44719     },
44720
44721     getVMaxSize : function(){
44722          var cmax = this.config.maxSize || 10000;
44723          var center = this.mgr.getRegion("center");
44724          return Math.min(cmax, (this.el.getHeight()+center.getEl().getHeight())-center.getMinHeight());
44725     },
44726
44727     onSplitMove : function(split, newSize){
44728         this.fireEvent("resized", this, newSize);
44729     },
44730     
44731     /** 
44732      * Returns the {@link Roo.SplitBar} for this region.
44733      * @return {Roo.SplitBar}
44734      */
44735     getSplitBar : function(){
44736         return this.split;
44737     },
44738     
44739     hide : function(){
44740         this.hideSplitter();
44741         Roo.bootstrap.layout.Split.superclass.hide.call(this);
44742     },
44743
44744     hideSplitter : function(){
44745         if(this.split){
44746             this.split.el.setLocation(-2000,-2000);
44747             this.split.el.hide();
44748         }
44749     },
44750
44751     show : function(){
44752         if(this.split){
44753             this.split.el.show();
44754         }
44755         Roo.bootstrap.layout.Split.superclass.show.call(this);
44756     },
44757     
44758     beforeSlide: function(){
44759         if(Roo.isGecko){// firefox overflow auto bug workaround
44760             this.bodyEl.clip();
44761             if(this.tabs) {
44762                 this.tabs.bodyEl.clip();
44763             }
44764             if(this.activePanel){
44765                 this.activePanel.getEl().clip();
44766                 
44767                 if(this.activePanel.beforeSlide){
44768                     this.activePanel.beforeSlide();
44769                 }
44770             }
44771         }
44772     },
44773     
44774     afterSlide : function(){
44775         if(Roo.isGecko){// firefox overflow auto bug workaround
44776             this.bodyEl.unclip();
44777             if(this.tabs) {
44778                 this.tabs.bodyEl.unclip();
44779             }
44780             if(this.activePanel){
44781                 this.activePanel.getEl().unclip();
44782                 if(this.activePanel.afterSlide){
44783                     this.activePanel.afterSlide();
44784                 }
44785             }
44786         }
44787     },
44788
44789     initAutoHide : function(){
44790         if(this.autoHide !== false){
44791             if(!this.autoHideHd){
44792                 var st = new Roo.util.DelayedTask(this.slideIn, this);
44793                 this.autoHideHd = {
44794                     "mouseout": function(e){
44795                         if(!e.within(this.el, true)){
44796                             st.delay(500);
44797                         }
44798                     },
44799                     "mouseover" : function(e){
44800                         st.cancel();
44801                     },
44802                     scope : this
44803                 };
44804             }
44805             this.el.on(this.autoHideHd);
44806         }
44807     },
44808
44809     clearAutoHide : function(){
44810         if(this.autoHide !== false){
44811             this.el.un("mouseout", this.autoHideHd.mouseout);
44812             this.el.un("mouseover", this.autoHideHd.mouseover);
44813         }
44814     },
44815
44816     clearMonitor : function(){
44817         Roo.get(document).un("click", this.slideInIf, this);
44818     },
44819
44820     // these names are backwards but not changed for compat
44821     slideOut : function(){
44822         if(this.isSlid || this.el.hasActiveFx()){
44823             return;
44824         }
44825         this.isSlid = true;
44826         if(this.collapseBtn){
44827             this.collapseBtn.hide();
44828         }
44829         this.closeBtnState = this.closeBtn.getStyle('display');
44830         this.closeBtn.hide();
44831         if(this.stickBtn){
44832             this.stickBtn.show();
44833         }
44834         this.el.show();
44835         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor());
44836         this.beforeSlide();
44837         this.el.setStyle("z-index", 10001);
44838         this.el.slideIn(this.getSlideAnchor(), {
44839             callback: function(){
44840                 this.afterSlide();
44841                 this.initAutoHide();
44842                 Roo.get(document).on("click", this.slideInIf, this);
44843                 this.fireEvent("slideshow", this);
44844             },
44845             scope: this,
44846             block: true
44847         });
44848     },
44849
44850     afterSlideIn : function(){
44851         this.clearAutoHide();
44852         this.isSlid = false;
44853         this.clearMonitor();
44854         this.el.setStyle("z-index", "");
44855         if(this.collapseBtn){
44856             this.collapseBtn.show();
44857         }
44858         this.closeBtn.setStyle('display', this.closeBtnState);
44859         if(this.stickBtn){
44860             this.stickBtn.hide();
44861         }
44862         this.fireEvent("slidehide", this);
44863     },
44864
44865     slideIn : function(cb){
44866         if(!this.isSlid || this.el.hasActiveFx()){
44867             Roo.callback(cb);
44868             return;
44869         }
44870         this.isSlid = false;
44871         this.beforeSlide();
44872         this.el.slideOut(this.getSlideAnchor(), {
44873             callback: function(){
44874                 this.el.setLeftTop(-10000, -10000);
44875                 this.afterSlide();
44876                 this.afterSlideIn();
44877                 Roo.callback(cb);
44878             },
44879             scope: this,
44880             block: true
44881         });
44882     },
44883     
44884     slideInIf : function(e){
44885         if(!e.within(this.el)){
44886             this.slideIn();
44887         }
44888     },
44889
44890     animateCollapse : function(){
44891         this.beforeSlide();
44892         this.el.setStyle("z-index", 20000);
44893         var anchor = this.getSlideAnchor();
44894         this.el.slideOut(anchor, {
44895             callback : function(){
44896                 this.el.setStyle("z-index", "");
44897                 this.collapsedEl.slideIn(anchor, {duration:.3});
44898                 this.afterSlide();
44899                 this.el.setLocation(-10000,-10000);
44900                 this.el.hide();
44901                 this.fireEvent("collapsed", this);
44902             },
44903             scope: this,
44904             block: true
44905         });
44906     },
44907
44908     animateExpand : function(){
44909         this.beforeSlide();
44910         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor(), this.getExpandAdj());
44911         this.el.setStyle("z-index", 20000);
44912         this.collapsedEl.hide({
44913             duration:.1
44914         });
44915         this.el.slideIn(this.getSlideAnchor(), {
44916             callback : function(){
44917                 this.el.setStyle("z-index", "");
44918                 this.afterSlide();
44919                 if(this.split){
44920                     this.split.el.show();
44921                 }
44922                 this.fireEvent("invalidated", this);
44923                 this.fireEvent("expanded", this);
44924             },
44925             scope: this,
44926             block: true
44927         });
44928     },
44929
44930     anchors : {
44931         "west" : "left",
44932         "east" : "right",
44933         "north" : "top",
44934         "south" : "bottom"
44935     },
44936
44937     sanchors : {
44938         "west" : "l",
44939         "east" : "r",
44940         "north" : "t",
44941         "south" : "b"
44942     },
44943
44944     canchors : {
44945         "west" : "tl-tr",
44946         "east" : "tr-tl",
44947         "north" : "tl-bl",
44948         "south" : "bl-tl"
44949     },
44950
44951     getAnchor : function(){
44952         return this.anchors[this.position];
44953     },
44954
44955     getCollapseAnchor : function(){
44956         return this.canchors[this.position];
44957     },
44958
44959     getSlideAnchor : function(){
44960         return this.sanchors[this.position];
44961     },
44962
44963     getAlignAdj : function(){
44964         var cm = this.cmargins;
44965         switch(this.position){
44966             case "west":
44967                 return [0, 0];
44968             break;
44969             case "east":
44970                 return [0, 0];
44971             break;
44972             case "north":
44973                 return [0, 0];
44974             break;
44975             case "south":
44976                 return [0, 0];
44977             break;
44978         }
44979     },
44980
44981     getExpandAdj : function(){
44982         var c = this.collapsedEl, cm = this.cmargins;
44983         switch(this.position){
44984             case "west":
44985                 return [-(cm.right+c.getWidth()+cm.left), 0];
44986             break;
44987             case "east":
44988                 return [cm.right+c.getWidth()+cm.left, 0];
44989             break;
44990             case "north":
44991                 return [0, -(cm.top+cm.bottom+c.getHeight())];
44992             break;
44993             case "south":
44994                 return [0, cm.top+cm.bottom+c.getHeight()];
44995             break;
44996         }
44997     }
44998 });/*
44999  * Based on:
45000  * Ext JS Library 1.1.1
45001  * Copyright(c) 2006-2007, Ext JS, LLC.
45002  *
45003  * Originally Released Under LGPL - original licence link has changed is not relivant.
45004  *
45005  * Fork - LGPL
45006  * <script type="text/javascript">
45007  */
45008 /*
45009  * These classes are private internal classes
45010  */
45011 Roo.bootstrap.layout.Center = function(config){
45012     config.region = "center";
45013     Roo.bootstrap.layout.Region.call(this, config);
45014     this.visible = true;
45015     this.minWidth = config.minWidth || 20;
45016     this.minHeight = config.minHeight || 20;
45017 };
45018
45019 Roo.extend(Roo.bootstrap.layout.Center, Roo.bootstrap.layout.Region, {
45020     hide : function(){
45021         // center panel can't be hidden
45022     },
45023     
45024     show : function(){
45025         // center panel can't be hidden
45026     },
45027     
45028     getMinWidth: function(){
45029         return this.minWidth;
45030     },
45031     
45032     getMinHeight: function(){
45033         return this.minHeight;
45034     }
45035 });
45036
45037
45038
45039
45040  
45041
45042
45043
45044
45045
45046
45047 Roo.bootstrap.layout.North = function(config)
45048 {
45049     config.region = 'north';
45050     config.cursor = 'n-resize';
45051     
45052     Roo.bootstrap.layout.Split.call(this, config);
45053     
45054     
45055     if(this.split){
45056         this.split.placement = Roo.bootstrap.SplitBar.TOP;
45057         this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
45058         this.split.el.addClass("roo-layout-split-v");
45059     }
45060     //var size = config.initialSize || config.height;
45061     //if(this.el && typeof size != "undefined"){
45062     //    this.el.setHeight(size);
45063     //}
45064 };
45065 Roo.extend(Roo.bootstrap.layout.North, Roo.bootstrap.layout.Split,
45066 {
45067     orientation: Roo.bootstrap.SplitBar.VERTICAL,
45068      
45069      
45070     onRender : function(ctr, pos)
45071     {
45072         Roo.bootstrap.layout.Split.prototype.onRender.call(this, ctr, pos);
45073         var size = this.config.initialSize || this.config.height;
45074         if(this.el && typeof size != "undefined"){
45075             this.el.setHeight(size);
45076         }
45077     
45078     },
45079     
45080     getBox : function(){
45081         if(this.collapsed){
45082             return this.collapsedEl.getBox();
45083         }
45084         var box = this.el.getBox();
45085         if(this.split){
45086             box.height += this.split.el.getHeight();
45087         }
45088         return box;
45089     },
45090     
45091     updateBox : function(box){
45092         if(this.split && !this.collapsed){
45093             box.height -= this.split.el.getHeight();
45094             this.split.el.setLeft(box.x);
45095             this.split.el.setTop(box.y+box.height);
45096             this.split.el.setWidth(box.width);
45097         }
45098         if(this.collapsed){
45099             this.updateBody(box.width, null);
45100         }
45101         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
45102     }
45103 });
45104
45105
45106
45107
45108
45109 Roo.bootstrap.layout.South = function(config){
45110     config.region = 'south';
45111     config.cursor = 's-resize';
45112     Roo.bootstrap.layout.Split.call(this, config);
45113     if(this.split){
45114         this.split.placement = Roo.bootstrap.SplitBar.BOTTOM;
45115         this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
45116         this.split.el.addClass("roo-layout-split-v");
45117     }
45118     
45119 };
45120
45121 Roo.extend(Roo.bootstrap.layout.South, Roo.bootstrap.layout.Split, {
45122     orientation: Roo.bootstrap.SplitBar.VERTICAL,
45123     
45124     onRender : function(ctr, pos)
45125     {
45126         Roo.bootstrap.layout.Split.prototype.onRender.call(this, ctr, pos);
45127         var size = this.config.initialSize || this.config.height;
45128         if(this.el && typeof size != "undefined"){
45129             this.el.setHeight(size);
45130         }
45131     
45132     },
45133     
45134     getBox : function(){
45135         if(this.collapsed){
45136             return this.collapsedEl.getBox();
45137         }
45138         var box = this.el.getBox();
45139         if(this.split){
45140             var sh = this.split.el.getHeight();
45141             box.height += sh;
45142             box.y -= sh;
45143         }
45144         return box;
45145     },
45146     
45147     updateBox : function(box){
45148         if(this.split && !this.collapsed){
45149             var sh = this.split.el.getHeight();
45150             box.height -= sh;
45151             box.y += sh;
45152             this.split.el.setLeft(box.x);
45153             this.split.el.setTop(box.y-sh);
45154             this.split.el.setWidth(box.width);
45155         }
45156         if(this.collapsed){
45157             this.updateBody(box.width, null);
45158         }
45159         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
45160     }
45161 });
45162
45163 Roo.bootstrap.layout.East = function(config){
45164     config.region = "east";
45165     config.cursor = "e-resize";
45166     Roo.bootstrap.layout.Split.call(this, config);
45167     if(this.split){
45168         this.split.placement = Roo.bootstrap.SplitBar.RIGHT;
45169         this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
45170         this.split.el.addClass("roo-layout-split-h");
45171     }
45172     
45173 };
45174 Roo.extend(Roo.bootstrap.layout.East, Roo.bootstrap.layout.Split, {
45175     orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
45176     
45177     onRender : function(ctr, pos)
45178     {
45179         Roo.bootstrap.layout.Split.prototype.onRender.call(this, ctr, pos);
45180         var size = this.config.initialSize || this.config.width;
45181         if(this.el && typeof size != "undefined"){
45182             this.el.setWidth(size);
45183         }
45184     
45185     },
45186     
45187     getBox : function(){
45188         if(this.collapsed){
45189             return this.collapsedEl.getBox();
45190         }
45191         var box = this.el.getBox();
45192         if(this.split){
45193             var sw = this.split.el.getWidth();
45194             box.width += sw;
45195             box.x -= sw;
45196         }
45197         return box;
45198     },
45199
45200     updateBox : function(box){
45201         if(this.split && !this.collapsed){
45202             var sw = this.split.el.getWidth();
45203             box.width -= sw;
45204             this.split.el.setLeft(box.x);
45205             this.split.el.setTop(box.y);
45206             this.split.el.setHeight(box.height);
45207             box.x += sw;
45208         }
45209         if(this.collapsed){
45210             this.updateBody(null, box.height);
45211         }
45212         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
45213     }
45214 });
45215
45216 Roo.bootstrap.layout.West = function(config){
45217     config.region = "west";
45218     config.cursor = "w-resize";
45219     
45220     Roo.bootstrap.layout.Split.call(this, config);
45221     if(this.split){
45222         this.split.placement = Roo.bootstrap.SplitBar.LEFT;
45223         this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
45224         this.split.el.addClass("roo-layout-split-h");
45225     }
45226     
45227 };
45228 Roo.extend(Roo.bootstrap.layout.West, Roo.bootstrap.layout.Split, {
45229     orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
45230     
45231     onRender: function(ctr, pos)
45232     {
45233         Roo.bootstrap.layout.West.superclass.onRender.call(this, ctr,pos);
45234         var size = this.config.initialSize || this.config.width;
45235         if(typeof size != "undefined"){
45236             this.el.setWidth(size);
45237         }
45238     },
45239     
45240     getBox : function(){
45241         if(this.collapsed){
45242             return this.collapsedEl.getBox();
45243         }
45244         var box = this.el.getBox();
45245         if (box.width == 0) {
45246             box.width = this.config.width; // kludge?
45247         }
45248         if(this.split){
45249             box.width += this.split.el.getWidth();
45250         }
45251         return box;
45252     },
45253     
45254     updateBox : function(box){
45255         if(this.split && !this.collapsed){
45256             var sw = this.split.el.getWidth();
45257             box.width -= sw;
45258             this.split.el.setLeft(box.x+box.width);
45259             this.split.el.setTop(box.y);
45260             this.split.el.setHeight(box.height);
45261         }
45262         if(this.collapsed){
45263             this.updateBody(null, box.height);
45264         }
45265         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
45266     }
45267 });/*
45268  * Based on:
45269  * Ext JS Library 1.1.1
45270  * Copyright(c) 2006-2007, Ext JS, LLC.
45271  *
45272  * Originally Released Under LGPL - original licence link has changed is not relivant.
45273  *
45274  * Fork - LGPL
45275  * <script type="text/javascript">
45276  */
45277 /**
45278  * @class Roo.bootstrap.paenl.Content
45279  * @extends Roo.util.Observable
45280  * @children Roo.bootstrap.Component
45281  * @parent builder Roo.bootstrap.layout.Border
45282  * A basic ContentPanel element. - a panel that contain any content (eg. forms etc.)
45283  * @cfg {Boolean}   fitToFrame    True for this panel to adjust its size to fit when the region resizes  (defaults to false)
45284  * @cfg {Boolean}   fitContainer   When using {@link #fitToFrame} and {@link #resizeEl}, you can also fit the parent container  (defaults to false)
45285  * @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
45286  * @cfg {Boolean}   closable      True if the panel can be closed/removed
45287  * @cfg {Boolean}   background    True if the panel should not be activated when it is added (defaults to false)
45288  * @cfg {String/HTMLElement/Element} resizeEl An element to resize if {@link #fitToFrame} is true (instead of this panel's element)
45289  * @cfg {Toolbar}   toolbar       A toolbar for this panel
45290  * @cfg {Boolean} autoScroll    True to scroll overflow in this panel (use with {@link #fitToFrame})
45291  * @cfg {String} title          The title for this panel
45292  * @cfg {Array} adjustments     Values to <b>add</b> to the width/height when doing a {@link #fitToFrame} (default is [0, 0])
45293  * @cfg {String} url            Calls {@link #setUrl} with this value
45294  * @cfg {String} region  [required] (center|north|south|east|west) which region to put this panel on (when used with xtype constructors)
45295  * @cfg {String/Object} params  When used with {@link #url}, calls {@link #setUrl} with this value
45296  * @cfg {Boolean} loadOnce      When used with {@link #url}, calls {@link #setUrl} with this value
45297  * @cfg {String}    content        Raw content to fill content panel with (uses setContent on construction.)
45298  * @cfg {Boolean} iframe      contents are an iframe - makes showing remote sources/CSS feasible..
45299  * @cfg {Boolean} badges render the badges
45300  * @cfg {String} cls  extra classes to use  
45301  * @cfg {String} background (primary|secondary|success|info|warning|danger|light|dark)
45302  
45303  * @constructor
45304  * Create a new ContentPanel.
45305  * @param {String/Object} config A string to set only the title or a config object
45306  
45307  */
45308 Roo.bootstrap.panel.Content = function( config){
45309     
45310     this.tpl = config.tpl || false;
45311     
45312     var el = config.el;
45313     var content = config.content;
45314
45315     if(config.autoCreate){ // xtype is available if this is called from factory
45316         el = Roo.id();
45317     }
45318     this.el = Roo.get(el);
45319     if(!this.el && config && config.autoCreate){
45320         if(typeof config.autoCreate == "object"){
45321             if(!config.autoCreate.id){
45322                 config.autoCreate.id = config.id||el;
45323             }
45324             this.el = Roo.DomHelper.append(document.body,
45325                         config.autoCreate, true);
45326         }else{
45327             var elcfg =  {
45328                 tag: "div",
45329                 cls: (config.cls || '') +
45330                     (config.background ? ' bg-' + config.background : '') +
45331                     " roo-layout-inactive-content",
45332                 id: config.id||el
45333             };
45334             if (config.iframe) {
45335                 elcfg.cn = [
45336                     {
45337                         tag : 'iframe',
45338                         style : 'border: 0px',
45339                         src : 'about:blank'
45340                     }
45341                 ];
45342             }
45343               
45344             if (config.html) {
45345                 elcfg.html = config.html;
45346                 
45347             }
45348                         
45349             this.el = Roo.DomHelper.append(document.body, elcfg , true);
45350             if (config.iframe) {
45351                 this.iframeEl = this.el.select('iframe',true).first();
45352             }
45353             
45354         }
45355     } 
45356     this.closable = false;
45357     this.loaded = false;
45358     this.active = false;
45359    
45360       
45361     if (config.toolbar && !config.toolbar.el && config.toolbar.xtype) {
45362         
45363         this.toolbar = new config.toolbar.xns[config.toolbar.xtype](config.toolbar);
45364         
45365         this.wrapEl = this.el; //this.el.wrap();
45366         var ti = [];
45367         if (config.toolbar.items) {
45368             ti = config.toolbar.items ;
45369             delete config.toolbar.items ;
45370         }
45371         
45372         var nitems = [];
45373         this.toolbar.render(this.wrapEl, 'before');
45374         for(var i =0;i < ti.length;i++) {
45375           //  Roo.log(['add child', items[i]]);
45376             nitems.push(this.toolbar.addxtype(Roo.apply({}, ti[i])));
45377         }
45378         this.toolbar.items = nitems;
45379         this.toolbar.el.insertBefore(this.wrapEl.dom.firstChild);
45380         delete config.toolbar;
45381         
45382     }
45383     /*
45384     // xtype created footer. - not sure if will work as we normally have to render first..
45385     if (this.footer && !this.footer.el && this.footer.xtype) {
45386         if (!this.wrapEl) {
45387             this.wrapEl = this.el.wrap();
45388         }
45389     
45390         this.footer.container = this.wrapEl.createChild();
45391          
45392         this.footer = Roo.factory(this.footer, Roo);
45393         
45394     }
45395     */
45396     
45397      if(typeof config == "string"){
45398         this.title = config;
45399     }else{
45400         Roo.apply(this, config);
45401     }
45402     
45403     if(this.resizeEl){
45404         this.resizeEl = Roo.get(this.resizeEl, true);
45405     }else{
45406         this.resizeEl = this.el;
45407     }
45408     // handle view.xtype
45409     
45410  
45411     
45412     
45413     this.addEvents({
45414         /**
45415          * @event activate
45416          * Fires when this panel is activated. 
45417          * @param {Roo.ContentPanel} this
45418          */
45419         "activate" : true,
45420         /**
45421          * @event deactivate
45422          * Fires when this panel is activated. 
45423          * @param {Roo.ContentPanel} this
45424          */
45425         "deactivate" : true,
45426
45427         /**
45428          * @event resize
45429          * Fires when this panel is resized if fitToFrame is true.
45430          * @param {Roo.ContentPanel} this
45431          * @param {Number} width The width after any component adjustments
45432          * @param {Number} height The height after any component adjustments
45433          */
45434         "resize" : true,
45435         
45436          /**
45437          * @event render
45438          * Fires when this tab is created
45439          * @param {Roo.ContentPanel} this
45440          */
45441         "render" : true,
45442         
45443           /**
45444          * @event scroll
45445          * Fires when this content is scrolled
45446          * @param {Roo.ContentPanel} this
45447          * @param {Event} scrollEvent
45448          */
45449         "scroll" : true
45450         
45451         
45452         
45453     });
45454     
45455
45456     
45457     
45458     if(this.autoScroll && !this.iframe){
45459         this.resizeEl.setStyle("overflow", "auto");
45460         this.resizeEl.on('scroll', this.onScroll, this);
45461     } else {
45462         // fix randome scrolling
45463         //this.el.on('scroll', function() {
45464         //    Roo.log('fix random scolling');
45465         //    this.scrollTo('top',0); 
45466         //});
45467     }
45468     content = content || this.content;
45469     if(content){
45470         this.setContent(content);
45471     }
45472     if(config && config.url){
45473         this.setUrl(this.url, this.params, this.loadOnce);
45474     }
45475     
45476     
45477     
45478     Roo.bootstrap.panel.Content.superclass.constructor.call(this);
45479     
45480     if (this.view && typeof(this.view.xtype) != 'undefined') {
45481         this.view.el = this.el.appendChild(document.createElement("div"));
45482         this.view = Roo.factory(this.view); 
45483         this.view.render  &&  this.view.render(false, '');  
45484     }
45485     
45486     
45487     this.fireEvent('render', this);
45488 };
45489
45490 Roo.extend(Roo.bootstrap.panel.Content, Roo.bootstrap.Component, {
45491     
45492     cls : '',
45493     background : '',
45494     
45495     tabTip : '',
45496     
45497     iframe : false,
45498     iframeEl : false,
45499     
45500     /* Resize Element - use this to work out scroll etc. */
45501     resizeEl : false,
45502     
45503     setRegion : function(region){
45504         this.region = region;
45505         this.setActiveClass(region && !this.background);
45506     },
45507     
45508     
45509     setActiveClass: function(state)
45510     {
45511         if(state){
45512            this.el.replaceClass("roo-layout-inactive-content", "roo-layout-active-content");
45513            this.el.setStyle('position','relative');
45514         }else{
45515            this.el.replaceClass("roo-layout-active-content", "roo-layout-inactive-content");
45516            this.el.setStyle('position', 'absolute');
45517         } 
45518     },
45519     
45520     /**
45521      * Returns the toolbar for this Panel if one was configured. 
45522      * @return {Roo.Toolbar} 
45523      */
45524     getToolbar : function(){
45525         return this.toolbar;
45526     },
45527     
45528     setActiveState : function(active)
45529     {
45530         this.active = active;
45531         this.setActiveClass(active);
45532         if(!active){
45533             if(this.fireEvent("deactivate", this) === false){
45534                 return false;
45535             }
45536             return true;
45537         }
45538         this.fireEvent("activate", this);
45539         return true;
45540     },
45541     /**
45542      * Updates this panel's element (not for iframe)
45543      * @param {String} content The new content
45544      * @param {Boolean} loadScripts (optional) true to look for and process scripts
45545     */
45546     setContent : function(content, loadScripts){
45547         if (this.iframe) {
45548             return;
45549         }
45550         
45551         this.el.update(content, loadScripts);
45552     },
45553
45554     ignoreResize : function(w, h)
45555     {
45556         //return false; // always resize?
45557         if(this.lastSize && this.lastSize.width == w && this.lastSize.height == h){
45558             return true;
45559         }else{
45560             this.lastSize = {width: w, height: h};
45561             return false;
45562         }
45563     },
45564     /**
45565      * Get the {@link Roo.UpdateManager} for this panel. Enables you to perform Ajax updates.
45566      * @return {Roo.UpdateManager} The UpdateManager
45567      */
45568     getUpdateManager : function(){
45569         if (this.iframe) {
45570             return false;
45571         }
45572         return this.el.getUpdateManager();
45573     },
45574      /**
45575      * Loads this content panel immediately with content from XHR. Note: to delay loading until the panel is activated, use {@link #setUrl}.
45576      * Does not work with IFRAME contents
45577      * @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:
45578 <pre><code>
45579 panel.load({
45580     url: "your-url.php",
45581     params: {param1: "foo", param2: "bar"}, // or a URL encoded string
45582     callback: yourFunction,
45583     scope: yourObject, //(optional scope)
45584     discardUrl: false,
45585     nocache: false,
45586     text: "Loading...",
45587     timeout: 30,
45588     scripts: false
45589 });
45590 </code></pre>
45591      
45592      * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
45593      * 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.
45594      * @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}
45595      * @param {Function} callback (optional) Callback when transaction is complete -- called with signature (oElement, bSuccess, oResponse)
45596      * @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.
45597      * @return {Roo.ContentPanel} this
45598      */
45599     load : function(){
45600         
45601         if (this.iframe) {
45602             return this;
45603         }
45604         
45605         var um = this.el.getUpdateManager();
45606         um.update.apply(um, arguments);
45607         return this;
45608     },
45609
45610
45611     /**
45612      * 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.
45613      * @param {String/Function} url The URL to load the content from or a function to call to get the URL
45614      * @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)
45615      * @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)
45616      * @return {Roo.UpdateManager|Boolean} The UpdateManager or false if IFRAME
45617      */
45618     setUrl : function(url, params, loadOnce){
45619         if (this.iframe) {
45620             this.iframeEl.dom.src = url;
45621             return false;
45622         }
45623         
45624         if(this.refreshDelegate){
45625             this.removeListener("activate", this.refreshDelegate);
45626         }
45627         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
45628         this.on("activate", this.refreshDelegate);
45629         return this.el.getUpdateManager();
45630     },
45631     
45632     _handleRefresh : function(url, params, loadOnce){
45633         if(!loadOnce || !this.loaded){
45634             var updater = this.el.getUpdateManager();
45635             updater.update(url, params, this._setLoaded.createDelegate(this));
45636         }
45637     },
45638     
45639     _setLoaded : function(){
45640         this.loaded = true;
45641     }, 
45642     
45643     /**
45644      * Returns this panel's id
45645      * @return {String} 
45646      */
45647     getId : function(){
45648         return this.el.id;
45649     },
45650     
45651     /** 
45652      * Returns this panel's element - used by regiosn to add.
45653      * @return {Roo.Element} 
45654      */
45655     getEl : function(){
45656         return this.wrapEl || this.el;
45657     },
45658     
45659    
45660     
45661     adjustForComponents : function(width, height)
45662     {
45663         //Roo.log('adjustForComponents ');
45664         if(this.resizeEl != this.el){
45665             width -= this.el.getFrameWidth('lr');
45666             height -= this.el.getFrameWidth('tb');
45667         }
45668         if(this.toolbar){
45669             var te = this.toolbar.getEl();
45670             te.setWidth(width);
45671             height -= te.getHeight();
45672         }
45673         if(this.footer){
45674             var te = this.footer.getEl();
45675             te.setWidth(width);
45676             height -= te.getHeight();
45677         }
45678         
45679         
45680         if(this.adjustments){
45681             width += this.adjustments[0];
45682             height += this.adjustments[1];
45683         }
45684         return {"width": width, "height": height};
45685     },
45686     
45687     setSize : function(width, height){
45688         if(this.fitToFrame && !this.ignoreResize(width, height)){
45689             if(this.fitContainer && this.resizeEl != this.el){
45690                 this.el.setSize(width, height);
45691             }
45692             var size = this.adjustForComponents(width, height);
45693             if (this.iframe) {
45694                 this.iframeEl.setSize(width,height);
45695             }
45696             
45697             this.resizeEl.setSize(this.autoWidth ? "auto" : size.width, this.autoHeight ? "auto" : size.height);
45698             this.fireEvent('resize', this, size.width, size.height);
45699             
45700             
45701         }
45702     },
45703     
45704     /**
45705      * Returns this panel's title
45706      * @return {String} 
45707      */
45708     getTitle : function(){
45709         
45710         if (typeof(this.title) != 'object') {
45711             return this.title;
45712         }
45713         
45714         var t = '';
45715         for (var k in this.title) {
45716             if (!this.title.hasOwnProperty(k)) {
45717                 continue;
45718             }
45719             
45720             if (k.indexOf('-') >= 0) {
45721                 var s = k.split('-');
45722                 for (var i = 0; i<s.length; i++) {
45723                     t += "<span class='visible-"+s[i]+"'>"+this.title[k]+"</span>";
45724                 }
45725             } else {
45726                 t += "<span class='visible-"+k+"'>"+this.title[k]+"</span>";
45727             }
45728         }
45729         return t;
45730     },
45731     
45732     /**
45733      * Set this panel's title
45734      * @param {String} title
45735      */
45736     setTitle : function(title){
45737         this.title = title;
45738         if(this.region){
45739             this.region.updatePanelTitle(this, title);
45740         }
45741     },
45742     
45743     /**
45744      * Returns true is this panel was configured to be closable
45745      * @return {Boolean} 
45746      */
45747     isClosable : function(){
45748         return this.closable;
45749     },
45750     
45751     beforeSlide : function(){
45752         this.el.clip();
45753         this.resizeEl.clip();
45754     },
45755     
45756     afterSlide : function(){
45757         this.el.unclip();
45758         this.resizeEl.unclip();
45759     },
45760     
45761     /**
45762      *   Force a content refresh from the URL specified in the {@link #setUrl} method.
45763      *   Will fail silently if the {@link #setUrl} method has not been called.
45764      *   This does not activate the panel, just updates its content.
45765      */
45766     refresh : function(){
45767         if(this.refreshDelegate){
45768            this.loaded = false;
45769            this.refreshDelegate();
45770         }
45771     },
45772     
45773     /**
45774      * Destroys this panel
45775      */
45776     destroy : function(){
45777         this.el.removeAllListeners();
45778         var tempEl = document.createElement("span");
45779         tempEl.appendChild(this.el.dom);
45780         tempEl.innerHTML = "";
45781         this.el.remove();
45782         this.el = null;
45783     },
45784     
45785     /**
45786      * form - if the content panel contains a form - this is a reference to it.
45787      * @type {Roo.form.Form}
45788      */
45789     form : false,
45790     /**
45791      * view - if the content panel contains a view (Roo.DatePicker / Roo.View / Roo.JsonView)
45792      *    This contains a reference to it.
45793      * @type {Roo.View}
45794      */
45795     view : false,
45796     
45797       /**
45798      * Adds a xtype elements to the panel - currently only supports Forms, View, JsonView.
45799      * <pre><code>
45800
45801 layout.addxtype({
45802        xtype : 'Form',
45803        items: [ .... ]
45804    }
45805 );
45806
45807 </code></pre>
45808      * @param {Object} cfg Xtype definition of item to add.
45809      */
45810     
45811     
45812     getChildContainer: function () {
45813         return this.getEl();
45814     },
45815     
45816     
45817     onScroll : function(e)
45818     {
45819         this.fireEvent('scroll', this, e);
45820     }
45821     
45822     
45823     /*
45824         var  ret = new Roo.factory(cfg);
45825         return ret;
45826         
45827         
45828         // add form..
45829         if (cfg.xtype.match(/^Form$/)) {
45830             
45831             var el;
45832             //if (this.footer) {
45833             //    el = this.footer.container.insertSibling(false, 'before');
45834             //} else {
45835                 el = this.el.createChild();
45836             //}
45837
45838             this.form = new  Roo.form.Form(cfg);
45839             
45840             
45841             if ( this.form.allItems.length) {
45842                 this.form.render(el.dom);
45843             }
45844             return this.form;
45845         }
45846         // should only have one of theses..
45847         if ([ 'View', 'JsonView', 'DatePicker'].indexOf(cfg.xtype) > -1) {
45848             // views.. should not be just added - used named prop 'view''
45849             
45850             cfg.el = this.el.appendChild(document.createElement("div"));
45851             // factory?
45852             
45853             var ret = new Roo.factory(cfg);
45854              
45855              ret.render && ret.render(false, ''); // render blank..
45856             this.view = ret;
45857             return ret;
45858         }
45859         return false;
45860     }
45861     \*/
45862 });
45863  
45864 /**
45865  * @class Roo.bootstrap.panel.Grid
45866  * @extends Roo.bootstrap.panel.Content
45867  * @constructor
45868  * Create a new GridPanel.
45869  * @cfg {Roo.bootstrap.Table} grid The grid for this panel
45870  * @cfg {Roo.bootstrap.nav.Simplebar} toolbar the toolbar at the top of the grid.
45871  * @param {Object} config A the config object
45872   
45873  */
45874
45875
45876
45877 Roo.bootstrap.panel.Grid = function(config)
45878 {
45879     
45880       
45881     this.wrapper = Roo.DomHelper.append(document.body, // wrapper for IE7 strict & safari scroll issue
45882         {tag: "div", cls: "roo-layout-grid-wrapper roo-layout-inactive-content"}, true);
45883
45884     config.el = this.wrapper;
45885     //this.el = this.wrapper;
45886     
45887       if (config.container) {
45888         // ctor'ed from a Border/panel.grid
45889         
45890         
45891         this.wrapper.setStyle("overflow", "hidden");
45892         this.wrapper.addClass('roo-grid-container');
45893
45894     }
45895     
45896     
45897     if(config.toolbar){
45898         var tool_el = this.wrapper.createChild();    
45899         this.toolbar = Roo.factory(config.toolbar);
45900         var ti = [];
45901         if (config.toolbar.items) {
45902             ti = config.toolbar.items ;
45903             delete config.toolbar.items ;
45904         }
45905         
45906         var nitems = [];
45907         this.toolbar.render(tool_el);
45908         for(var i =0;i < ti.length;i++) {
45909           //  Roo.log(['add child', items[i]]);
45910             nitems.push(this.toolbar.addxtype(Roo.apply({}, ti[i])));
45911         }
45912         this.toolbar.items = nitems;
45913         
45914         delete config.toolbar;
45915     }
45916     
45917     Roo.bootstrap.panel.Grid.superclass.constructor.call(this, config);
45918     config.grid.scrollBody = true;;
45919     config.grid.monitorWindowResize = false; // turn off autosizing
45920     config.grid.autoHeight = false;
45921     config.grid.autoWidth = false;
45922     
45923     this.grid = new config.grid.xns[config.grid.xtype](config.grid);
45924     
45925     if (config.background) {
45926         // render grid on panel activation (if panel background)
45927         this.on('activate', function(gp) {
45928             if (!gp.grid.rendered) {
45929                 gp.grid.render(this.wrapper);
45930                 gp.grid.getGridEl().replaceClass("roo-layout-inactive-content", "roo-layout-component-panel");   
45931             }
45932         });
45933             
45934     } else {
45935         this.grid.render(this.wrapper);
45936         this.grid.getGridEl().replaceClass("roo-layout-inactive-content", "roo-layout-component-panel");               
45937
45938     }
45939     //this.wrapper.dom.appendChild(config.grid.getGridEl().dom);
45940     // ??? needed ??? config.el = this.wrapper;
45941     
45942     
45943     
45944   
45945     // xtype created footer. - not sure if will work as we normally have to render first..
45946     if (this.footer && !this.footer.el && this.footer.xtype) {
45947         
45948         var ctr = this.grid.getView().getFooterPanel(true);
45949         this.footer.dataSource = this.grid.dataSource;
45950         this.footer = Roo.factory(this.footer, Roo);
45951         this.footer.render(ctr);
45952         
45953     }
45954     
45955     
45956     
45957     
45958      
45959 };
45960
45961 Roo.extend(Roo.bootstrap.panel.Grid, Roo.bootstrap.panel.Content,
45962 {
45963   
45964     getId : function(){
45965         return this.grid.id;
45966     },
45967     
45968     /**
45969      * Returns the grid for this panel
45970      * @return {Roo.bootstrap.Table} 
45971      */
45972     getGrid : function(){
45973         return this.grid;    
45974     },
45975     
45976     setSize : function(width, height)
45977     {
45978      
45979         //if(!this.ignoreResize(width, height)){
45980             var grid = this.grid;
45981             var size = this.adjustForComponents(width, height);
45982             // tfoot is not a footer?
45983           
45984             
45985             var gridel = grid.getGridEl();
45986             gridel.setSize(size.width, size.height);
45987             
45988             var tbd = grid.getGridEl().select('tbody', true).first();
45989             var thd = grid.getGridEl().select('thead',true).first();
45990             var tbf= grid.getGridEl().select('tfoot', true).first();
45991
45992             if (tbf) {
45993                 size.height -= tbf.getHeight();
45994             }
45995             if (thd) {
45996                 size.height -= thd.getHeight();
45997             }
45998             
45999             tbd.setSize(size.width, size.height );
46000             // this is for the account management tab -seems to work there.
46001             var thd = grid.getGridEl().select('thead',true).first();
46002             //if (tbd) {
46003             //    tbd.setSize(size.width, size.height - thd.getHeight());
46004             //}
46005              
46006             grid.autoSize();
46007         //}
46008    
46009     },
46010      
46011     
46012     
46013     beforeSlide : function(){
46014         this.grid.getView().scroller.clip();
46015     },
46016     
46017     afterSlide : function(){
46018         this.grid.getView().scroller.unclip();
46019     },
46020     
46021     destroy : function(){
46022         this.grid.destroy();
46023         delete this.grid;
46024         Roo.bootstrap.panel.Grid.superclass.destroy.call(this); 
46025     }
46026 });
46027
46028 /**
46029  * @class Roo.bootstrap.panel.Nest
46030  * @extends Roo.bootstrap.panel.Content
46031  * @constructor
46032  * Create a new Panel, that can contain a layout.Border.
46033  * 
46034  * 
46035  * @param {String/Object} config A string to set only the title or a config object
46036  */
46037 Roo.bootstrap.panel.Nest = function(config)
46038 {
46039     // construct with only one argument..
46040     /* FIXME - implement nicer consturctors
46041     if (layout.layout) {
46042         config = layout;
46043         layout = config.layout;
46044         delete config.layout;
46045     }
46046     if (layout.xtype && !layout.getEl) {
46047         // then layout needs constructing..
46048         layout = Roo.factory(layout, Roo);
46049     }
46050     */
46051     
46052     config.el =  config.layout.getEl();
46053     
46054     Roo.bootstrap.panel.Nest.superclass.constructor.call(this, config);
46055     
46056     config.layout.monitorWindowResize = false; // turn off autosizing
46057     this.layout = config.layout;
46058     this.layout.getEl().addClass("roo-layout-nested-layout");
46059     this.layout.parent = this;
46060     
46061     
46062     
46063     
46064 };
46065
46066 Roo.extend(Roo.bootstrap.panel.Nest, Roo.bootstrap.panel.Content, {
46067     /**
46068     * @cfg {Roo.BorderLayout} layout The layout for this panel
46069     */
46070     layout : false,
46071
46072     setSize : function(width, height){
46073         if(!this.ignoreResize(width, height)){
46074             var size = this.adjustForComponents(width, height);
46075             var el = this.layout.getEl();
46076             if (size.height < 1) {
46077                 el.setWidth(size.width);   
46078             } else {
46079                 el.setSize(size.width, size.height);
46080             }
46081             var touch = el.dom.offsetWidth;
46082             this.layout.layout();
46083             // ie requires a double layout on the first pass
46084             if(Roo.isIE && !this.initialized){
46085                 this.initialized = true;
46086                 this.layout.layout();
46087             }
46088         }
46089     },
46090     
46091     // activate all subpanels if not currently active..
46092     
46093     setActiveState : function(active){
46094         this.active = active;
46095         this.setActiveClass(active);
46096         
46097         if(!active){
46098             this.fireEvent("deactivate", this);
46099             return;
46100         }
46101         
46102         this.fireEvent("activate", this);
46103         // not sure if this should happen before or after..
46104         if (!this.layout) {
46105             return; // should not happen..
46106         }
46107         var reg = false;
46108         for (var r in this.layout.regions) {
46109             reg = this.layout.getRegion(r);
46110             if (reg.getActivePanel()) {
46111                 //reg.showPanel(reg.getActivePanel()); // force it to activate.. 
46112                 reg.setActivePanel(reg.getActivePanel());
46113                 continue;
46114             }
46115             if (!reg.panels.length) {
46116                 continue;
46117             }
46118             reg.showPanel(reg.getPanel(0));
46119         }
46120         
46121         
46122         
46123         
46124     },
46125     
46126     /**
46127      * Returns the nested BorderLayout for this panel
46128      * @return {Roo.BorderLayout} 
46129      */
46130     getLayout : function(){
46131         return this.layout;
46132     },
46133     
46134      /**
46135      * Adds a xtype elements to the layout of the nested panel
46136      * <pre><code>
46137
46138 panel.addxtype({
46139        xtype : 'ContentPanel',
46140        region: 'west',
46141        items: [ .... ]
46142    }
46143 );
46144
46145 panel.addxtype({
46146         xtype : 'NestedLayoutPanel',
46147         region: 'west',
46148         layout: {
46149            center: { },
46150            west: { }   
46151         },
46152         items : [ ... list of content panels or nested layout panels.. ]
46153    }
46154 );
46155 </code></pre>
46156      * @param {Object} cfg Xtype definition of item to add.
46157      */
46158     addxtype : function(cfg) {
46159         return this.layout.addxtype(cfg);
46160     
46161     }
46162 });/*
46163  * Based on:
46164  * Ext JS Library 1.1.1
46165  * Copyright(c) 2006-2007, Ext JS, LLC.
46166  *
46167  * Originally Released Under LGPL - original licence link has changed is not relivant.
46168  *
46169  * Fork - LGPL
46170  * <script type="text/javascript">
46171  */
46172 /**
46173  * @class Roo.TabPanel
46174  * @extends Roo.util.Observable
46175  * A lightweight tab container.
46176  * <br><br>
46177  * Usage:
46178  * <pre><code>
46179 // basic tabs 1, built from existing content
46180 var tabs = new Roo.TabPanel("tabs1");
46181 tabs.addTab("script", "View Script");
46182 tabs.addTab("markup", "View Markup");
46183 tabs.activate("script");
46184
46185 // more advanced tabs, built from javascript
46186 var jtabs = new Roo.TabPanel("jtabs");
46187 jtabs.addTab("jtabs-1", "Normal Tab", "My content was added during construction.");
46188
46189 // set up the UpdateManager
46190 var tab2 = jtabs.addTab("jtabs-2", "Ajax Tab 1");
46191 var updater = tab2.getUpdateManager();
46192 updater.setDefaultUrl("ajax1.htm");
46193 tab2.on('activate', updater.refresh, updater, true);
46194
46195 // Use setUrl for Ajax loading
46196 var tab3 = jtabs.addTab("jtabs-3", "Ajax Tab 2");
46197 tab3.setUrl("ajax2.htm", null, true);
46198
46199 // Disabled tab
46200 var tab4 = jtabs.addTab("tabs1-5", "Disabled Tab", "Can't see me cause I'm disabled");
46201 tab4.disable();
46202
46203 jtabs.activate("jtabs-1");
46204  * </code></pre>
46205  * @constructor
46206  * Create a new TabPanel.
46207  * @param {String/HTMLElement/Roo.Element} container The id, DOM element or Roo.Element container where this TabPanel is to be rendered.
46208  * @param {Object/Boolean} config Config object to set any properties for this TabPanel, or true to render the tabs on the bottom.
46209  */
46210 Roo.bootstrap.panel.Tabs = function(config){
46211     /**
46212     * The container element for this TabPanel.
46213     * @type Roo.Element
46214     */
46215     this.el = Roo.get(config.el);
46216     delete config.el;
46217     if(config){
46218         if(typeof config == "boolean"){
46219             this.tabPosition = config ? "bottom" : "top";
46220         }else{
46221             Roo.apply(this, config);
46222         }
46223     }
46224     
46225     if(this.tabPosition == "bottom"){
46226         // if tabs are at the bottom = create the body first.
46227         this.bodyEl = Roo.get(this.createBody(this.el.dom));
46228         this.el.addClass("roo-tabs-bottom");
46229     }
46230     // next create the tabs holders
46231     
46232     if (this.tabPosition == "west"){
46233         
46234         var reg = this.region; // fake it..
46235         while (reg) {
46236             if (!reg.mgr.parent) {
46237                 break;
46238             }
46239             reg = reg.mgr.parent.region;
46240         }
46241         Roo.log("got nest?");
46242         Roo.log(reg);
46243         if (reg.mgr.getRegion('west')) {
46244             var ctrdom = reg.mgr.getRegion('west').bodyEl.dom;
46245             this.stripWrap = Roo.get(this.createStrip(ctrdom ), true);
46246             this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
46247             this.stripEl.setVisibilityMode(Roo.Element.DISPLAY);
46248             this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
46249         
46250             
46251         }
46252         
46253         
46254     } else {
46255      
46256         this.stripWrap = Roo.get(this.createStrip(this.el.dom), true);
46257         this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
46258         this.stripEl.setVisibilityMode(Roo.Element.DISPLAY);
46259         this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
46260     }
46261     
46262     
46263     if(Roo.isIE){
46264         Roo.fly(this.stripWrap.dom.firstChild).setStyle("overflow-x", "hidden");
46265     }
46266     
46267     // finally - if tabs are at the top, then create the body last..
46268     if(this.tabPosition != "bottom"){
46269         /** The body element that contains {@link Roo.TabPanelItem} bodies. +
46270          * @type Roo.Element
46271          */
46272         this.bodyEl = Roo.get(this.createBody(this.el.dom));
46273         this.el.addClass("roo-tabs-top");
46274     }
46275     this.items = [];
46276
46277     this.bodyEl.setStyle("position", "relative");
46278
46279     this.active = null;
46280     this.activateDelegate = this.activate.createDelegate(this);
46281
46282     this.addEvents({
46283         /**
46284          * @event tabchange
46285          * Fires when the active tab changes
46286          * @param {Roo.TabPanel} this
46287          * @param {Roo.TabPanelItem} activePanel The new active tab
46288          */
46289         "tabchange": true,
46290         /**
46291          * @event beforetabchange
46292          * Fires before the active tab changes, set cancel to true on the "e" parameter to cancel the change
46293          * @param {Roo.TabPanel} this
46294          * @param {Object} e Set cancel to true on this object to cancel the tab change
46295          * @param {Roo.TabPanelItem} tab The tab being changed to
46296          */
46297         "beforetabchange" : true
46298     });
46299
46300     Roo.EventManager.onWindowResize(this.onResize, this);
46301     this.cpad = this.el.getPadding("lr");
46302     this.hiddenCount = 0;
46303
46304
46305     // toolbar on the tabbar support...
46306     if (this.toolbar) {
46307         alert("no toolbar support yet");
46308         this.toolbar  = false;
46309         /*
46310         var tcfg = this.toolbar;
46311         tcfg.container = this.stripEl.child('td.x-tab-strip-toolbar');  
46312         this.toolbar = new Roo.Toolbar(tcfg);
46313         if (Roo.isSafari) {
46314             var tbl = tcfg.container.child('table', true);
46315             tbl.setAttribute('width', '100%');
46316         }
46317         */
46318         
46319     }
46320    
46321
46322
46323     Roo.bootstrap.panel.Tabs.superclass.constructor.call(this);
46324 };
46325
46326 Roo.extend(Roo.bootstrap.panel.Tabs, Roo.util.Observable, {
46327     /*
46328      *@cfg {String} tabPosition "top" or "bottom" (defaults to "top")
46329      */
46330     tabPosition : "top",
46331     /*
46332      *@cfg {Number} currentTabWidth The width of the current tab (defaults to 0)
46333      */
46334     currentTabWidth : 0,
46335     /*
46336      *@cfg {Number} minTabWidth The minimum width of a tab (defaults to 40) (ignored if {@link #resizeTabs} is not true)
46337      */
46338     minTabWidth : 40,
46339     /*
46340      *@cfg {Number} maxTabWidth The maximum width of a tab (defaults to 250) (ignored if {@link #resizeTabs} is not true)
46341      */
46342     maxTabWidth : 250,
46343     /*
46344      *@cfg {Number} preferredTabWidth The preferred (default) width of a tab (defaults to 175) (ignored if {@link #resizeTabs} is not true)
46345      */
46346     preferredTabWidth : 175,
46347     /*
46348      *@cfg {Boolean} resizeTabs True to enable dynamic tab resizing (defaults to false)
46349      */
46350     resizeTabs : false,
46351     /*
46352      *@cfg {Boolean} monitorResize Set this to true to turn on window resize monitoring (ignored if {@link #resizeTabs} is not true) (defaults to true)
46353      */
46354     monitorResize : true,
46355     /*
46356      *@cfg {Object} toolbar xtype description of toolbar to show at the right of the tab bar. 
46357      */
46358     toolbar : false,  // set by caller..
46359     
46360     region : false, /// set by caller
46361     
46362     disableTooltips : true, // not used yet...
46363
46364     /**
46365      * Creates a new {@link Roo.TabPanelItem} by looking for an existing element with the provided id -- if it's not found it creates one.
46366      * @param {String} id The id of the div to use <b>or create</b>
46367      * @param {String} text The text for the tab
46368      * @param {String} content (optional) Content to put in the TabPanelItem body
46369      * @param {Boolean} closable (optional) True to create a close icon on the tab
46370      * @return {Roo.TabPanelItem} The created TabPanelItem
46371      */
46372     addTab : function(id, text, content, closable, tpl)
46373     {
46374         var item = new Roo.bootstrap.panel.TabItem({
46375             panel: this,
46376             id : id,
46377             text : text,
46378             closable : closable,
46379             tpl : tpl
46380         });
46381         this.addTabItem(item);
46382         if(content){
46383             item.setContent(content);
46384         }
46385         return item;
46386     },
46387
46388     /**
46389      * Returns the {@link Roo.TabPanelItem} with the specified id/index
46390      * @param {String/Number} id The id or index of the TabPanelItem to fetch.
46391      * @return {Roo.TabPanelItem}
46392      */
46393     getTab : function(id){
46394         return this.items[id];
46395     },
46396
46397     /**
46398      * Hides the {@link Roo.TabPanelItem} with the specified id/index
46399      * @param {String/Number} id The id or index of the TabPanelItem to hide.
46400      */
46401     hideTab : function(id){
46402         var t = this.items[id];
46403         if(!t.isHidden()){
46404            t.setHidden(true);
46405            this.hiddenCount++;
46406            this.autoSizeTabs();
46407         }
46408     },
46409
46410     /**
46411      * "Unhides" the {@link Roo.TabPanelItem} with the specified id/index.
46412      * @param {String/Number} id The id or index of the TabPanelItem to unhide.
46413      */
46414     unhideTab : function(id){
46415         var t = this.items[id];
46416         if(t.isHidden()){
46417            t.setHidden(false);
46418            this.hiddenCount--;
46419            this.autoSizeTabs();
46420         }
46421     },
46422
46423     /**
46424      * Adds an existing {@link Roo.TabPanelItem}.
46425      * @param {Roo.TabPanelItem} item The TabPanelItem to add
46426      */
46427     addTabItem : function(item)
46428     {
46429         this.items[item.id] = item;
46430         this.items.push(item);
46431         this.autoSizeTabs();
46432       //  if(this.resizeTabs){
46433     //       item.setWidth(this.currentTabWidth || this.preferredTabWidth);
46434   //         this.autoSizeTabs();
46435 //        }else{
46436 //            item.autoSize();
46437        // }
46438     },
46439
46440     /**
46441      * Removes a {@link Roo.TabPanelItem}.
46442      * @param {String/Number} id The id or index of the TabPanelItem to remove.
46443      */
46444     removeTab : function(id){
46445         var items = this.items;
46446         var tab = items[id];
46447         if(!tab) { return; }
46448         var index = items.indexOf(tab);
46449         if(this.active == tab && items.length > 1){
46450             var newTab = this.getNextAvailable(index);
46451             if(newTab) {
46452                 newTab.activate();
46453             }
46454         }
46455         this.stripEl.dom.removeChild(tab.pnode.dom);
46456         if(tab.bodyEl.dom.parentNode == this.bodyEl.dom){ // if it was moved already prevent error
46457             this.bodyEl.dom.removeChild(tab.bodyEl.dom);
46458         }
46459         items.splice(index, 1);
46460         delete this.items[tab.id];
46461         tab.fireEvent("close", tab);
46462         tab.purgeListeners();
46463         this.autoSizeTabs();
46464     },
46465
46466     getNextAvailable : function(start){
46467         var items = this.items;
46468         var index = start;
46469         // look for a next tab that will slide over to
46470         // replace the one being removed
46471         while(index < items.length){
46472             var item = items[++index];
46473             if(item && !item.isHidden()){
46474                 return item;
46475             }
46476         }
46477         // if one isn't found select the previous tab (on the left)
46478         index = start;
46479         while(index >= 0){
46480             var item = items[--index];
46481             if(item && !item.isHidden()){
46482                 return item;
46483             }
46484         }
46485         return null;
46486     },
46487
46488     /**
46489      * Disables a {@link Roo.TabPanelItem}. It cannot be the active tab, if it is this call is ignored.
46490      * @param {String/Number} id The id or index of the TabPanelItem to disable.
46491      */
46492     disableTab : function(id){
46493         var tab = this.items[id];
46494         if(tab && this.active != tab){
46495             tab.disable();
46496         }
46497     },
46498
46499     /**
46500      * Enables a {@link Roo.TabPanelItem} that is disabled.
46501      * @param {String/Number} id The id or index of the TabPanelItem to enable.
46502      */
46503     enableTab : function(id){
46504         var tab = this.items[id];
46505         tab.enable();
46506     },
46507
46508     /**
46509      * Activates a {@link Roo.TabPanelItem}. The currently active one will be deactivated.
46510      * @param {String/Number} id The id or index of the TabPanelItem to activate.
46511      * @return {Roo.TabPanelItem} The TabPanelItem.
46512      */
46513     activate : function(id)
46514     {
46515         //Roo.log('activite:'  + id);
46516         
46517         var tab = this.items[id];
46518         if(!tab){
46519             return null;
46520         }
46521         if(tab == this.active || tab.disabled){
46522             return tab;
46523         }
46524         var e = {};
46525         this.fireEvent("beforetabchange", this, e, tab);
46526         if(e.cancel !== true && !tab.disabled){
46527             if(this.active){
46528                 this.active.hide();
46529             }
46530             this.active = this.items[id];
46531             this.active.show();
46532             this.fireEvent("tabchange", this, this.active);
46533         }
46534         return tab;
46535     },
46536
46537     /**
46538      * Gets the active {@link Roo.TabPanelItem}.
46539      * @return {Roo.TabPanelItem} The active TabPanelItem or null if none are active.
46540      */
46541     getActiveTab : function(){
46542         return this.active;
46543     },
46544
46545     /**
46546      * Updates the tab body element to fit the height of the container element
46547      * for overflow scrolling
46548      * @param {Number} targetHeight (optional) Override the starting height from the elements height
46549      */
46550     syncHeight : function(targetHeight){
46551         var height = (targetHeight || this.el.getHeight())-this.el.getBorderWidth("tb")-this.el.getPadding("tb");
46552         var bm = this.bodyEl.getMargins();
46553         var newHeight = height-(this.stripWrap.getHeight()||0)-(bm.top+bm.bottom);
46554         this.bodyEl.setHeight(newHeight);
46555         return newHeight;
46556     },
46557
46558     onResize : function(){
46559         if(this.monitorResize){
46560             this.autoSizeTabs();
46561         }
46562     },
46563
46564     /**
46565      * Disables tab resizing while tabs are being added (if {@link #resizeTabs} is false this does nothing)
46566      */
46567     beginUpdate : function(){
46568         this.updating = true;
46569     },
46570
46571     /**
46572      * Stops an update and resizes the tabs (if {@link #resizeTabs} is false this does nothing)
46573      */
46574     endUpdate : function(){
46575         this.updating = false;
46576         this.autoSizeTabs();
46577     },
46578
46579     /**
46580      * Manual call to resize the tabs (if {@link #resizeTabs} is false this does nothing)
46581      */
46582     autoSizeTabs : function()
46583     {
46584         var count = this.items.length;
46585         var vcount = count - this.hiddenCount;
46586         
46587         if (vcount < 2) {
46588             this.stripEl.hide();
46589         } else {
46590             this.stripEl.show();
46591         }
46592         
46593         if(!this.resizeTabs || count < 1 || vcount < 1 || this.updating) {
46594             return;
46595         }
46596         
46597         
46598         var w = Math.max(this.el.getWidth() - this.cpad, 10);
46599         var availWidth = Math.floor(w / vcount);
46600         var b = this.stripBody;
46601         if(b.getWidth() > w){
46602             var tabs = this.items;
46603             this.setTabWidth(Math.max(availWidth, this.minTabWidth)-2);
46604             if(availWidth < this.minTabWidth){
46605                 /*if(!this.sleft){    // incomplete scrolling code
46606                     this.createScrollButtons();
46607                 }
46608                 this.showScroll();
46609                 this.stripClip.setWidth(w - (this.sleft.getWidth()+this.sright.getWidth()));*/
46610             }
46611         }else{
46612             if(this.currentTabWidth < this.preferredTabWidth){
46613                 this.setTabWidth(Math.min(availWidth, this.preferredTabWidth)-2);
46614             }
46615         }
46616     },
46617
46618     /**
46619      * Returns the number of tabs in this TabPanel.
46620      * @return {Number}
46621      */
46622      getCount : function(){
46623          return this.items.length;
46624      },
46625
46626     /**
46627      * Resizes all the tabs to the passed width
46628      * @param {Number} The new width
46629      */
46630     setTabWidth : function(width){
46631         this.currentTabWidth = width;
46632         for(var i = 0, len = this.items.length; i < len; i++) {
46633                 if(!this.items[i].isHidden()) {
46634                 this.items[i].setWidth(width);
46635             }
46636         }
46637     },
46638
46639     /**
46640      * Destroys this TabPanel
46641      * @param {Boolean} removeEl (optional) True to remove the element from the DOM as well (defaults to undefined)
46642      */
46643     destroy : function(removeEl){
46644         Roo.EventManager.removeResizeListener(this.onResize, this);
46645         for(var i = 0, len = this.items.length; i < len; i++){
46646             this.items[i].purgeListeners();
46647         }
46648         if(removeEl === true){
46649             this.el.update("");
46650             this.el.remove();
46651         }
46652     },
46653     
46654     createStrip : function(container)
46655     {
46656         var strip = document.createElement("nav");
46657         strip.className = Roo.bootstrap.version == 4 ?
46658             "navbar-light bg-light" : 
46659             "navbar navbar-default"; //"x-tabs-wrap";
46660         container.appendChild(strip);
46661         return strip;
46662     },
46663     
46664     createStripList : function(strip)
46665     {
46666         // div wrapper for retard IE
46667         // returns the "tr" element.
46668         strip.innerHTML = '<ul class="nav nav-tabs" role="tablist"></ul>';
46669         //'<div class="x-tabs-strip-wrap">'+
46670           //  '<table class="x-tabs-strip" cellspacing="0" cellpadding="0" border="0"><tbody><tr>'+
46671           //  '<td class="x-tab-strip-toolbar"></td></tr></tbody></table></div>';
46672         return strip.firstChild; //.firstChild.firstChild.firstChild;
46673     },
46674     createBody : function(container)
46675     {
46676         var body = document.createElement("div");
46677         Roo.id(body, "tab-body");
46678         //Roo.fly(body).addClass("x-tabs-body");
46679         Roo.fly(body).addClass("tab-content");
46680         container.appendChild(body);
46681         return body;
46682     },
46683     createItemBody :function(bodyEl, id){
46684         var body = Roo.getDom(id);
46685         if(!body){
46686             body = document.createElement("div");
46687             body.id = id;
46688         }
46689         //Roo.fly(body).addClass("x-tabs-item-body");
46690         Roo.fly(body).addClass("tab-pane");
46691          bodyEl.insertBefore(body, bodyEl.firstChild);
46692         return body;
46693     },
46694     /** @private */
46695     createStripElements :  function(stripEl, text, closable, tpl)
46696     {
46697         var td = document.createElement("li"); // was td..
46698         td.className = 'nav-item';
46699         
46700         //stripEl.insertBefore(td, stripEl.childNodes[stripEl.childNodes.length-1]);
46701         
46702         
46703         stripEl.appendChild(td);
46704         /*if(closable){
46705             td.className = "x-tabs-closable";
46706             if(!this.closeTpl){
46707                 this.closeTpl = new Roo.Template(
46708                    '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
46709                    '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span>' +
46710                    '<div unselectable="on" class="close-icon">&#160;</div></em></span></a>'
46711                 );
46712             }
46713             var el = this.closeTpl.overwrite(td, {"text": text});
46714             var close = el.getElementsByTagName("div")[0];
46715             var inner = el.getElementsByTagName("em")[0];
46716             return {"el": el, "close": close, "inner": inner};
46717         } else {
46718         */
46719         // not sure what this is..
46720 //            if(!this.tabTpl){
46721                 //this.tabTpl = new Roo.Template(
46722                 //   '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
46723                 //   '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span></em></span></a>'
46724                 //);
46725 //                this.tabTpl = new Roo.Template(
46726 //                   '<a href="#">' +
46727 //                   '<span unselectable="on"' +
46728 //                            (this.disableTooltips ? '' : ' title="{text}"') +
46729 //                            ' >{text}</span></a>'
46730 //                );
46731 //                
46732 //            }
46733
46734
46735             var template = tpl || this.tabTpl || false;
46736             
46737             if(!template){
46738                 template =  new Roo.Template(
46739                         Roo.bootstrap.version == 4 ? 
46740                             (
46741                                 '<a class="nav-link" href="#" unselectable="on"' +
46742                                      (this.disableTooltips ? '' : ' title="{text}"') +
46743                                      ' >{text}</a>'
46744                             ) : (
46745                                 '<a class="nav-link" href="#">' +
46746                                 '<span unselectable="on"' +
46747                                          (this.disableTooltips ? '' : ' title="{text}"') +
46748                                     ' >{text}</span></a>'
46749                             )
46750                 );
46751             }
46752             
46753             switch (typeof(template)) {
46754                 case 'object' :
46755                     break;
46756                 case 'string' :
46757                     template = new Roo.Template(template);
46758                     break;
46759                 default :
46760                     break;
46761             }
46762             
46763             var el = template.overwrite(td, {"text": text});
46764             
46765             var inner = el.getElementsByTagName("span")[0];
46766             
46767             return {"el": el, "inner": inner};
46768             
46769     }
46770         
46771     
46772 });
46773
46774 /**
46775  * @class Roo.TabPanelItem
46776  * @extends Roo.util.Observable
46777  * Represents an individual item (tab plus body) in a TabPanel.
46778  * @param {Roo.TabPanel} tabPanel The {@link Roo.TabPanel} this TabPanelItem belongs to
46779  * @param {String} id The id of this TabPanelItem
46780  * @param {String} text The text for the tab of this TabPanelItem
46781  * @param {Boolean} closable True to allow this TabPanelItem to be closable (defaults to false)
46782  */
46783 Roo.bootstrap.panel.TabItem = function(config){
46784     /**
46785      * The {@link Roo.TabPanel} this TabPanelItem belongs to
46786      * @type Roo.TabPanel
46787      */
46788     this.tabPanel = config.panel;
46789     /**
46790      * The id for this TabPanelItem
46791      * @type String
46792      */
46793     this.id = config.id;
46794     /** @private */
46795     this.disabled = false;
46796     /** @private */
46797     this.text = config.text;
46798     /** @private */
46799     this.loaded = false;
46800     this.closable = config.closable;
46801
46802     /**
46803      * The body element for this TabPanelItem.
46804      * @type Roo.Element
46805      */
46806     this.bodyEl = Roo.get(this.tabPanel.createItemBody(this.tabPanel.bodyEl.dom, config.id));
46807     this.bodyEl.setVisibilityMode(Roo.Element.VISIBILITY);
46808     this.bodyEl.setStyle("display", "block");
46809     this.bodyEl.setStyle("zoom", "1");
46810     //this.hideAction();
46811
46812     var els = this.tabPanel.createStripElements(this.tabPanel.stripEl.dom, config.text, config.closable, config.tpl);
46813     /** @private */
46814     this.el = Roo.get(els.el);
46815     this.inner = Roo.get(els.inner, true);
46816      this.textEl = Roo.bootstrap.version == 4 ?
46817         this.el : Roo.get(this.el.dom.firstChild, true);
46818
46819     this.pnode = this.linode = Roo.get(els.el.parentNode, true);
46820     this.status_node = Roo.bootstrap.version == 4 ? this.el : this.linode;
46821
46822     
46823 //    this.el.on("mousedown", this.onTabMouseDown, this);
46824     this.el.on("click", this.onTabClick, this);
46825     /** @private */
46826     if(config.closable){
46827         var c = Roo.get(els.close, true);
46828         c.dom.title = this.closeText;
46829         c.addClassOnOver("close-over");
46830         c.on("click", this.closeClick, this);
46831      }
46832
46833     this.addEvents({
46834          /**
46835          * @event activate
46836          * Fires when this tab becomes the active tab.
46837          * @param {Roo.TabPanel} tabPanel The parent TabPanel
46838          * @param {Roo.TabPanelItem} this
46839          */
46840         "activate": true,
46841         /**
46842          * @event beforeclose
46843          * Fires before this tab is closed. To cancel the close, set cancel to true on e (e.cancel = true).
46844          * @param {Roo.TabPanelItem} this
46845          * @param {Object} e Set cancel to true on this object to cancel the close.
46846          */
46847         "beforeclose": true,
46848         /**
46849          * @event close
46850          * Fires when this tab is closed.
46851          * @param {Roo.TabPanelItem} this
46852          */
46853          "close": true,
46854         /**
46855          * @event deactivate
46856          * Fires when this tab is no longer the active tab.
46857          * @param {Roo.TabPanel} tabPanel The parent TabPanel
46858          * @param {Roo.TabPanelItem} this
46859          */
46860          "deactivate" : true
46861     });
46862     this.hidden = false;
46863
46864     Roo.bootstrap.panel.TabItem.superclass.constructor.call(this);
46865 };
46866
46867 Roo.extend(Roo.bootstrap.panel.TabItem, Roo.util.Observable,
46868            {
46869     purgeListeners : function(){
46870        Roo.util.Observable.prototype.purgeListeners.call(this);
46871        this.el.removeAllListeners();
46872     },
46873     /**
46874      * Shows this TabPanelItem -- this <b>does not</b> deactivate the currently active TabPanelItem.
46875      */
46876     show : function(){
46877         this.status_node.addClass("active");
46878         this.showAction();
46879         if(Roo.isOpera){
46880             this.tabPanel.stripWrap.repaint();
46881         }
46882         this.fireEvent("activate", this.tabPanel, this);
46883     },
46884
46885     /**
46886      * Returns true if this tab is the active tab.
46887      * @return {Boolean}
46888      */
46889     isActive : function(){
46890         return this.tabPanel.getActiveTab() == this;
46891     },
46892
46893     /**
46894      * Hides this TabPanelItem -- if you don't activate another TabPanelItem this could look odd.
46895      */
46896     hide : function(){
46897         this.status_node.removeClass("active");
46898         this.hideAction();
46899         this.fireEvent("deactivate", this.tabPanel, this);
46900     },
46901
46902     hideAction : function(){
46903         this.bodyEl.hide();
46904         this.bodyEl.setStyle("position", "absolute");
46905         this.bodyEl.setLeft("-20000px");
46906         this.bodyEl.setTop("-20000px");
46907     },
46908
46909     showAction : function(){
46910         this.bodyEl.setStyle("position", "relative");
46911         this.bodyEl.setTop("");
46912         this.bodyEl.setLeft("");
46913         this.bodyEl.show();
46914     },
46915
46916     /**
46917      * Set the tooltip for the tab.
46918      * @param {String} tooltip The tab's tooltip
46919      */
46920     setTooltip : function(text){
46921         if(Roo.QuickTips && Roo.QuickTips.isEnabled()){
46922             this.textEl.dom.qtip = text;
46923             this.textEl.dom.removeAttribute('title');
46924         }else{
46925             this.textEl.dom.title = text;
46926         }
46927     },
46928
46929     onTabClick : function(e){
46930         e.preventDefault();
46931         this.tabPanel.activate(this.id);
46932     },
46933
46934     onTabMouseDown : function(e){
46935         e.preventDefault();
46936         this.tabPanel.activate(this.id);
46937     },
46938 /*
46939     getWidth : function(){
46940         return this.inner.getWidth();
46941     },
46942
46943     setWidth : function(width){
46944         var iwidth = width - this.linode.getPadding("lr");
46945         this.inner.setWidth(iwidth);
46946         this.textEl.setWidth(iwidth-this.inner.getPadding("lr"));
46947         this.linode.setWidth(width);
46948     },
46949 */
46950     /**
46951      * Show or hide the tab
46952      * @param {Boolean} hidden True to hide or false to show.
46953      */
46954     setHidden : function(hidden){
46955         this.hidden = hidden;
46956         this.linode.setStyle("display", hidden ? "none" : "");
46957     },
46958
46959     /**
46960      * Returns true if this tab is "hidden"
46961      * @return {Boolean}
46962      */
46963     isHidden : function(){
46964         return this.hidden;
46965     },
46966
46967     /**
46968      * Returns the text for this tab
46969      * @return {String}
46970      */
46971     getText : function(){
46972         return this.text;
46973     },
46974     /*
46975     autoSize : function(){
46976         //this.el.beginMeasure();
46977         this.textEl.setWidth(1);
46978         /*
46979          *  #2804 [new] Tabs in Roojs
46980          *  increase the width by 2-4 pixels to prevent the ellipssis showing in chrome
46981          */
46982         //this.setWidth(this.textEl.dom.scrollWidth+this.linode.getPadding("lr")+this.inner.getPadding("lr") + 2);
46983         //this.el.endMeasure();
46984     //},
46985
46986     /**
46987      * Sets the text for the tab (Note: this also sets the tooltip text)
46988      * @param {String} text The tab's text and tooltip
46989      */
46990     setText : function(text){
46991         this.text = text;
46992         this.textEl.update(text);
46993         this.setTooltip(text);
46994         //if(!this.tabPanel.resizeTabs){
46995         //    this.autoSize();
46996         //}
46997     },
46998     /**
46999      * Activates this TabPanelItem -- this <b>does</b> deactivate the currently active TabPanelItem.
47000      */
47001     activate : function(){
47002         this.tabPanel.activate(this.id);
47003     },
47004
47005     /**
47006      * Disables this TabPanelItem -- this does nothing if this is the active TabPanelItem.
47007      */
47008     disable : function(){
47009         if(this.tabPanel.active != this){
47010             this.disabled = true;
47011             this.status_node.addClass("disabled");
47012         }
47013     },
47014
47015     /**
47016      * Enables this TabPanelItem if it was previously disabled.
47017      */
47018     enable : function(){
47019         this.disabled = false;
47020         this.status_node.removeClass("disabled");
47021     },
47022
47023     /**
47024      * Sets the content for this TabPanelItem.
47025      * @param {String} content The content
47026      * @param {Boolean} loadScripts true to look for and load scripts
47027      */
47028     setContent : function(content, loadScripts){
47029         this.bodyEl.update(content, loadScripts);
47030     },
47031
47032     /**
47033      * Gets the {@link Roo.UpdateManager} for the body of this TabPanelItem. Enables you to perform Ajax updates.
47034      * @return {Roo.UpdateManager} The UpdateManager
47035      */
47036     getUpdateManager : function(){
47037         return this.bodyEl.getUpdateManager();
47038     },
47039
47040     /**
47041      * Set a URL to be used to load the content for this TabPanelItem.
47042      * @param {String/Function} url The URL to load the content from, or a function to call to get the URL
47043      * @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)
47044      * @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)
47045      * @return {Roo.UpdateManager} The UpdateManager
47046      */
47047     setUrl : function(url, params, loadOnce){
47048         if(this.refreshDelegate){
47049             this.un('activate', this.refreshDelegate);
47050         }
47051         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
47052         this.on("activate", this.refreshDelegate);
47053         return this.bodyEl.getUpdateManager();
47054     },
47055
47056     /** @private */
47057     _handleRefresh : function(url, params, loadOnce){
47058         if(!loadOnce || !this.loaded){
47059             var updater = this.bodyEl.getUpdateManager();
47060             updater.update(url, params, this._setLoaded.createDelegate(this));
47061         }
47062     },
47063
47064     /**
47065      *   Forces a content refresh from the URL specified in the {@link #setUrl} method.
47066      *   Will fail silently if the setUrl method has not been called.
47067      *   This does not activate the panel, just updates its content.
47068      */
47069     refresh : function(){
47070         if(this.refreshDelegate){
47071            this.loaded = false;
47072            this.refreshDelegate();
47073         }
47074     },
47075
47076     /** @private */
47077     _setLoaded : function(){
47078         this.loaded = true;
47079     },
47080
47081     /** @private */
47082     closeClick : function(e){
47083         var o = {};
47084         e.stopEvent();
47085         this.fireEvent("beforeclose", this, o);
47086         if(o.cancel !== true){
47087             this.tabPanel.removeTab(this.id);
47088         }
47089     },
47090     /**
47091      * The text displayed in the tooltip for the close icon.
47092      * @type String
47093      */
47094     closeText : "Close this tab"
47095 });
47096 /**
47097 *    This script refer to:
47098 *    Title: International Telephone Input
47099 *    Author: Jack O'Connor
47100 *    Code version:  v12.1.12
47101 *    Availability: https://github.com/jackocnr/intl-tel-input.git
47102 **/
47103
47104 Roo.bootstrap.form.PhoneInputData = function() {
47105     var d = [
47106       [
47107         "Afghanistan (‫افغانستان‬‎)",
47108         "af",
47109         "93"
47110       ],
47111       [
47112         "Albania (Shqipëri)",
47113         "al",
47114         "355"
47115       ],
47116       [
47117         "Algeria (‫الجزائر‬‎)",
47118         "dz",
47119         "213"
47120       ],
47121       [
47122         "American Samoa",
47123         "as",
47124         "1684"
47125       ],
47126       [
47127         "Andorra",
47128         "ad",
47129         "376"
47130       ],
47131       [
47132         "Angola",
47133         "ao",
47134         "244"
47135       ],
47136       [
47137         "Anguilla",
47138         "ai",
47139         "1264"
47140       ],
47141       [
47142         "Antigua and Barbuda",
47143         "ag",
47144         "1268"
47145       ],
47146       [
47147         "Argentina",
47148         "ar",
47149         "54"
47150       ],
47151       [
47152         "Armenia (Հայաստան)",
47153         "am",
47154         "374"
47155       ],
47156       [
47157         "Aruba",
47158         "aw",
47159         "297"
47160       ],
47161       [
47162         "Australia",
47163         "au",
47164         "61",
47165         0
47166       ],
47167       [
47168         "Austria (Österreich)",
47169         "at",
47170         "43"
47171       ],
47172       [
47173         "Azerbaijan (Azərbaycan)",
47174         "az",
47175         "994"
47176       ],
47177       [
47178         "Bahamas",
47179         "bs",
47180         "1242"
47181       ],
47182       [
47183         "Bahrain (‫البحرين‬‎)",
47184         "bh",
47185         "973"
47186       ],
47187       [
47188         "Bangladesh (বাংলাদেশ)",
47189         "bd",
47190         "880"
47191       ],
47192       [
47193         "Barbados",
47194         "bb",
47195         "1246"
47196       ],
47197       [
47198         "Belarus (Беларусь)",
47199         "by",
47200         "375"
47201       ],
47202       [
47203         "Belgium (België)",
47204         "be",
47205         "32"
47206       ],
47207       [
47208         "Belize",
47209         "bz",
47210         "501"
47211       ],
47212       [
47213         "Benin (Bénin)",
47214         "bj",
47215         "229"
47216       ],
47217       [
47218         "Bermuda",
47219         "bm",
47220         "1441"
47221       ],
47222       [
47223         "Bhutan (འབྲུག)",
47224         "bt",
47225         "975"
47226       ],
47227       [
47228         "Bolivia",
47229         "bo",
47230         "591"
47231       ],
47232       [
47233         "Bosnia and Herzegovina (Босна и Херцеговина)",
47234         "ba",
47235         "387"
47236       ],
47237       [
47238         "Botswana",
47239         "bw",
47240         "267"
47241       ],
47242       [
47243         "Brazil (Brasil)",
47244         "br",
47245         "55"
47246       ],
47247       [
47248         "British Indian Ocean Territory",
47249         "io",
47250         "246"
47251       ],
47252       [
47253         "British Virgin Islands",
47254         "vg",
47255         "1284"
47256       ],
47257       [
47258         "Brunei",
47259         "bn",
47260         "673"
47261       ],
47262       [
47263         "Bulgaria (България)",
47264         "bg",
47265         "359"
47266       ],
47267       [
47268         "Burkina Faso",
47269         "bf",
47270         "226"
47271       ],
47272       [
47273         "Burundi (Uburundi)",
47274         "bi",
47275         "257"
47276       ],
47277       [
47278         "Cambodia (កម្ពុជា)",
47279         "kh",
47280         "855"
47281       ],
47282       [
47283         "Cameroon (Cameroun)",
47284         "cm",
47285         "237"
47286       ],
47287       [
47288         "Canada",
47289         "ca",
47290         "1",
47291         1,
47292         ["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"]
47293       ],
47294       [
47295         "Cape Verde (Kabu Verdi)",
47296         "cv",
47297         "238"
47298       ],
47299       [
47300         "Caribbean Netherlands",
47301         "bq",
47302         "599",
47303         1
47304       ],
47305       [
47306         "Cayman Islands",
47307         "ky",
47308         "1345"
47309       ],
47310       [
47311         "Central African Republic (République centrafricaine)",
47312         "cf",
47313         "236"
47314       ],
47315       [
47316         "Chad (Tchad)",
47317         "td",
47318         "235"
47319       ],
47320       [
47321         "Chile",
47322         "cl",
47323         "56"
47324       ],
47325       [
47326         "China (中国)",
47327         "cn",
47328         "86"
47329       ],
47330       [
47331         "Christmas Island",
47332         "cx",
47333         "61",
47334         2
47335       ],
47336       [
47337         "Cocos (Keeling) Islands",
47338         "cc",
47339         "61",
47340         1
47341       ],
47342       [
47343         "Colombia",
47344         "co",
47345         "57"
47346       ],
47347       [
47348         "Comoros (‫جزر القمر‬‎)",
47349         "km",
47350         "269"
47351       ],
47352       [
47353         "Congo (DRC) (Jamhuri ya Kidemokrasia ya Kongo)",
47354         "cd",
47355         "243"
47356       ],
47357       [
47358         "Congo (Republic) (Congo-Brazzaville)",
47359         "cg",
47360         "242"
47361       ],
47362       [
47363         "Cook Islands",
47364         "ck",
47365         "682"
47366       ],
47367       [
47368         "Costa Rica",
47369         "cr",
47370         "506"
47371       ],
47372       [
47373         "Côte d’Ivoire",
47374         "ci",
47375         "225"
47376       ],
47377       [
47378         "Croatia (Hrvatska)",
47379         "hr",
47380         "385"
47381       ],
47382       [
47383         "Cuba",
47384         "cu",
47385         "53"
47386       ],
47387       [
47388         "Curaçao",
47389         "cw",
47390         "599",
47391         0
47392       ],
47393       [
47394         "Cyprus (Κύπρος)",
47395         "cy",
47396         "357"
47397       ],
47398       [
47399         "Czech Republic (Česká republika)",
47400         "cz",
47401         "420"
47402       ],
47403       [
47404         "Denmark (Danmark)",
47405         "dk",
47406         "45"
47407       ],
47408       [
47409         "Djibouti",
47410         "dj",
47411         "253"
47412       ],
47413       [
47414         "Dominica",
47415         "dm",
47416         "1767"
47417       ],
47418       [
47419         "Dominican Republic (República Dominicana)",
47420         "do",
47421         "1",
47422         2,
47423         ["809", "829", "849"]
47424       ],
47425       [
47426         "Ecuador",
47427         "ec",
47428         "593"
47429       ],
47430       [
47431         "Egypt (‫مصر‬‎)",
47432         "eg",
47433         "20"
47434       ],
47435       [
47436         "El Salvador",
47437         "sv",
47438         "503"
47439       ],
47440       [
47441         "Equatorial Guinea (Guinea Ecuatorial)",
47442         "gq",
47443         "240"
47444       ],
47445       [
47446         "Eritrea",
47447         "er",
47448         "291"
47449       ],
47450       [
47451         "Estonia (Eesti)",
47452         "ee",
47453         "372"
47454       ],
47455       [
47456         "Ethiopia",
47457         "et",
47458         "251"
47459       ],
47460       [
47461         "Falkland Islands (Islas Malvinas)",
47462         "fk",
47463         "500"
47464       ],
47465       [
47466         "Faroe Islands (Føroyar)",
47467         "fo",
47468         "298"
47469       ],
47470       [
47471         "Fiji",
47472         "fj",
47473         "679"
47474       ],
47475       [
47476         "Finland (Suomi)",
47477         "fi",
47478         "358",
47479         0
47480       ],
47481       [
47482         "France",
47483         "fr",
47484         "33"
47485       ],
47486       [
47487         "French Guiana (Guyane française)",
47488         "gf",
47489         "594"
47490       ],
47491       [
47492         "French Polynesia (Polynésie française)",
47493         "pf",
47494         "689"
47495       ],
47496       [
47497         "Gabon",
47498         "ga",
47499         "241"
47500       ],
47501       [
47502         "Gambia",
47503         "gm",
47504         "220"
47505       ],
47506       [
47507         "Georgia (საქართველო)",
47508         "ge",
47509         "995"
47510       ],
47511       [
47512         "Germany (Deutschland)",
47513         "de",
47514         "49"
47515       ],
47516       [
47517         "Ghana (Gaana)",
47518         "gh",
47519         "233"
47520       ],
47521       [
47522         "Gibraltar",
47523         "gi",
47524         "350"
47525       ],
47526       [
47527         "Greece (Ελλάδα)",
47528         "gr",
47529         "30"
47530       ],
47531       [
47532         "Greenland (Kalaallit Nunaat)",
47533         "gl",
47534         "299"
47535       ],
47536       [
47537         "Grenada",
47538         "gd",
47539         "1473"
47540       ],
47541       [
47542         "Guadeloupe",
47543         "gp",
47544         "590",
47545         0
47546       ],
47547       [
47548         "Guam",
47549         "gu",
47550         "1671"
47551       ],
47552       [
47553         "Guatemala",
47554         "gt",
47555         "502"
47556       ],
47557       [
47558         "Guernsey",
47559         "gg",
47560         "44",
47561         1
47562       ],
47563       [
47564         "Guinea (Guinée)",
47565         "gn",
47566         "224"
47567       ],
47568       [
47569         "Guinea-Bissau (Guiné Bissau)",
47570         "gw",
47571         "245"
47572       ],
47573       [
47574         "Guyana",
47575         "gy",
47576         "592"
47577       ],
47578       [
47579         "Haiti",
47580         "ht",
47581         "509"
47582       ],
47583       [
47584         "Honduras",
47585         "hn",
47586         "504"
47587       ],
47588       [
47589         "Hong Kong (香港)",
47590         "hk",
47591         "852"
47592       ],
47593       [
47594         "Hungary (Magyarország)",
47595         "hu",
47596         "36"
47597       ],
47598       [
47599         "Iceland (Ísland)",
47600         "is",
47601         "354"
47602       ],
47603       [
47604         "India (भारत)",
47605         "in",
47606         "91"
47607       ],
47608       [
47609         "Indonesia",
47610         "id",
47611         "62"
47612       ],
47613       [
47614         "Iran (‫ایران‬‎)",
47615         "ir",
47616         "98"
47617       ],
47618       [
47619         "Iraq (‫العراق‬‎)",
47620         "iq",
47621         "964"
47622       ],
47623       [
47624         "Ireland",
47625         "ie",
47626         "353"
47627       ],
47628       [
47629         "Isle of Man",
47630         "im",
47631         "44",
47632         2
47633       ],
47634       [
47635         "Israel (‫ישראל‬‎)",
47636         "il",
47637         "972"
47638       ],
47639       [
47640         "Italy (Italia)",
47641         "it",
47642         "39",
47643         0
47644       ],
47645       [
47646         "Jamaica",
47647         "jm",
47648         "1876"
47649       ],
47650       [
47651         "Japan (日本)",
47652         "jp",
47653         "81"
47654       ],
47655       [
47656         "Jersey",
47657         "je",
47658         "44",
47659         3
47660       ],
47661       [
47662         "Jordan (‫الأردن‬‎)",
47663         "jo",
47664         "962"
47665       ],
47666       [
47667         "Kazakhstan (Казахстан)",
47668         "kz",
47669         "7",
47670         1
47671       ],
47672       [
47673         "Kenya",
47674         "ke",
47675         "254"
47676       ],
47677       [
47678         "Kiribati",
47679         "ki",
47680         "686"
47681       ],
47682       [
47683         "Kosovo",
47684         "xk",
47685         "383"
47686       ],
47687       [
47688         "Kuwait (‫الكويت‬‎)",
47689         "kw",
47690         "965"
47691       ],
47692       [
47693         "Kyrgyzstan (Кыргызстан)",
47694         "kg",
47695         "996"
47696       ],
47697       [
47698         "Laos (ລາວ)",
47699         "la",
47700         "856"
47701       ],
47702       [
47703         "Latvia (Latvija)",
47704         "lv",
47705         "371"
47706       ],
47707       [
47708         "Lebanon (‫لبنان‬‎)",
47709         "lb",
47710         "961"
47711       ],
47712       [
47713         "Lesotho",
47714         "ls",
47715         "266"
47716       ],
47717       [
47718         "Liberia",
47719         "lr",
47720         "231"
47721       ],
47722       [
47723         "Libya (‫ليبيا‬‎)",
47724         "ly",
47725         "218"
47726       ],
47727       [
47728         "Liechtenstein",
47729         "li",
47730         "423"
47731       ],
47732       [
47733         "Lithuania (Lietuva)",
47734         "lt",
47735         "370"
47736       ],
47737       [
47738         "Luxembourg",
47739         "lu",
47740         "352"
47741       ],
47742       [
47743         "Macau (澳門)",
47744         "mo",
47745         "853"
47746       ],
47747       [
47748         "Macedonia (FYROM) (Македонија)",
47749         "mk",
47750         "389"
47751       ],
47752       [
47753         "Madagascar (Madagasikara)",
47754         "mg",
47755         "261"
47756       ],
47757       [
47758         "Malawi",
47759         "mw",
47760         "265"
47761       ],
47762       [
47763         "Malaysia",
47764         "my",
47765         "60"
47766       ],
47767       [
47768         "Maldives",
47769         "mv",
47770         "960"
47771       ],
47772       [
47773         "Mali",
47774         "ml",
47775         "223"
47776       ],
47777       [
47778         "Malta",
47779         "mt",
47780         "356"
47781       ],
47782       [
47783         "Marshall Islands",
47784         "mh",
47785         "692"
47786       ],
47787       [
47788         "Martinique",
47789         "mq",
47790         "596"
47791       ],
47792       [
47793         "Mauritania (‫موريتانيا‬‎)",
47794         "mr",
47795         "222"
47796       ],
47797       [
47798         "Mauritius (Moris)",
47799         "mu",
47800         "230"
47801       ],
47802       [
47803         "Mayotte",
47804         "yt",
47805         "262",
47806         1
47807       ],
47808       [
47809         "Mexico (México)",
47810         "mx",
47811         "52"
47812       ],
47813       [
47814         "Micronesia",
47815         "fm",
47816         "691"
47817       ],
47818       [
47819         "Moldova (Republica Moldova)",
47820         "md",
47821         "373"
47822       ],
47823       [
47824         "Monaco",
47825         "mc",
47826         "377"
47827       ],
47828       [
47829         "Mongolia (Монгол)",
47830         "mn",
47831         "976"
47832       ],
47833       [
47834         "Montenegro (Crna Gora)",
47835         "me",
47836         "382"
47837       ],
47838       [
47839         "Montserrat",
47840         "ms",
47841         "1664"
47842       ],
47843       [
47844         "Morocco (‫المغرب‬‎)",
47845         "ma",
47846         "212",
47847         0
47848       ],
47849       [
47850         "Mozambique (Moçambique)",
47851         "mz",
47852         "258"
47853       ],
47854       [
47855         "Myanmar (Burma) (မြန်မာ)",
47856         "mm",
47857         "95"
47858       ],
47859       [
47860         "Namibia (Namibië)",
47861         "na",
47862         "264"
47863       ],
47864       [
47865         "Nauru",
47866         "nr",
47867         "674"
47868       ],
47869       [
47870         "Nepal (नेपाल)",
47871         "np",
47872         "977"
47873       ],
47874       [
47875         "Netherlands (Nederland)",
47876         "nl",
47877         "31"
47878       ],
47879       [
47880         "New Caledonia (Nouvelle-Calédonie)",
47881         "nc",
47882         "687"
47883       ],
47884       [
47885         "New Zealand",
47886         "nz",
47887         "64"
47888       ],
47889       [
47890         "Nicaragua",
47891         "ni",
47892         "505"
47893       ],
47894       [
47895         "Niger (Nijar)",
47896         "ne",
47897         "227"
47898       ],
47899       [
47900         "Nigeria",
47901         "ng",
47902         "234"
47903       ],
47904       [
47905         "Niue",
47906         "nu",
47907         "683"
47908       ],
47909       [
47910         "Norfolk Island",
47911         "nf",
47912         "672"
47913       ],
47914       [
47915         "North Korea (조선 민주주의 인민 공화국)",
47916         "kp",
47917         "850"
47918       ],
47919       [
47920         "Northern Mariana Islands",
47921         "mp",
47922         "1670"
47923       ],
47924       [
47925         "Norway (Norge)",
47926         "no",
47927         "47",
47928         0
47929       ],
47930       [
47931         "Oman (‫عُمان‬‎)",
47932         "om",
47933         "968"
47934       ],
47935       [
47936         "Pakistan (‫پاکستان‬‎)",
47937         "pk",
47938         "92"
47939       ],
47940       [
47941         "Palau",
47942         "pw",
47943         "680"
47944       ],
47945       [
47946         "Palestine (‫فلسطين‬‎)",
47947         "ps",
47948         "970"
47949       ],
47950       [
47951         "Panama (Panamá)",
47952         "pa",
47953         "507"
47954       ],
47955       [
47956         "Papua New Guinea",
47957         "pg",
47958         "675"
47959       ],
47960       [
47961         "Paraguay",
47962         "py",
47963         "595"
47964       ],
47965       [
47966         "Peru (Perú)",
47967         "pe",
47968         "51"
47969       ],
47970       [
47971         "Philippines",
47972         "ph",
47973         "63"
47974       ],
47975       [
47976         "Poland (Polska)",
47977         "pl",
47978         "48"
47979       ],
47980       [
47981         "Portugal",
47982         "pt",
47983         "351"
47984       ],
47985       [
47986         "Puerto Rico",
47987         "pr",
47988         "1",
47989         3,
47990         ["787", "939"]
47991       ],
47992       [
47993         "Qatar (‫قطر‬‎)",
47994         "qa",
47995         "974"
47996       ],
47997       [
47998         "Réunion (La Réunion)",
47999         "re",
48000         "262",
48001         0
48002       ],
48003       [
48004         "Romania (România)",
48005         "ro",
48006         "40"
48007       ],
48008       [
48009         "Russia (Россия)",
48010         "ru",
48011         "7",
48012         0
48013       ],
48014       [
48015         "Rwanda",
48016         "rw",
48017         "250"
48018       ],
48019       [
48020         "Saint Barthélemy",
48021         "bl",
48022         "590",
48023         1
48024       ],
48025       [
48026         "Saint Helena",
48027         "sh",
48028         "290"
48029       ],
48030       [
48031         "Saint Kitts and Nevis",
48032         "kn",
48033         "1869"
48034       ],
48035       [
48036         "Saint Lucia",
48037         "lc",
48038         "1758"
48039       ],
48040       [
48041         "Saint Martin (Saint-Martin (partie française))",
48042         "mf",
48043         "590",
48044         2
48045       ],
48046       [
48047         "Saint Pierre and Miquelon (Saint-Pierre-et-Miquelon)",
48048         "pm",
48049         "508"
48050       ],
48051       [
48052         "Saint Vincent and the Grenadines",
48053         "vc",
48054         "1784"
48055       ],
48056       [
48057         "Samoa",
48058         "ws",
48059         "685"
48060       ],
48061       [
48062         "San Marino",
48063         "sm",
48064         "378"
48065       ],
48066       [
48067         "São Tomé and Príncipe (São Tomé e Príncipe)",
48068         "st",
48069         "239"
48070       ],
48071       [
48072         "Saudi Arabia (‫المملكة العربية السعودية‬‎)",
48073         "sa",
48074         "966"
48075       ],
48076       [
48077         "Senegal (Sénégal)",
48078         "sn",
48079         "221"
48080       ],
48081       [
48082         "Serbia (Србија)",
48083         "rs",
48084         "381"
48085       ],
48086       [
48087         "Seychelles",
48088         "sc",
48089         "248"
48090       ],
48091       [
48092         "Sierra Leone",
48093         "sl",
48094         "232"
48095       ],
48096       [
48097         "Singapore",
48098         "sg",
48099         "65"
48100       ],
48101       [
48102         "Sint Maarten",
48103         "sx",
48104         "1721"
48105       ],
48106       [
48107         "Slovakia (Slovensko)",
48108         "sk",
48109         "421"
48110       ],
48111       [
48112         "Slovenia (Slovenija)",
48113         "si",
48114         "386"
48115       ],
48116       [
48117         "Solomon Islands",
48118         "sb",
48119         "677"
48120       ],
48121       [
48122         "Somalia (Soomaaliya)",
48123         "so",
48124         "252"
48125       ],
48126       [
48127         "South Africa",
48128         "za",
48129         "27"
48130       ],
48131       [
48132         "South Korea (대한민국)",
48133         "kr",
48134         "82"
48135       ],
48136       [
48137         "South Sudan (‫جنوب السودان‬‎)",
48138         "ss",
48139         "211"
48140       ],
48141       [
48142         "Spain (España)",
48143         "es",
48144         "34"
48145       ],
48146       [
48147         "Sri Lanka (ශ්‍රී ලංකාව)",
48148         "lk",
48149         "94"
48150       ],
48151       [
48152         "Sudan (‫السودان‬‎)",
48153         "sd",
48154         "249"
48155       ],
48156       [
48157         "Suriname",
48158         "sr",
48159         "597"
48160       ],
48161       [
48162         "Svalbard and Jan Mayen",
48163         "sj",
48164         "47",
48165         1
48166       ],
48167       [
48168         "Swaziland",
48169         "sz",
48170         "268"
48171       ],
48172       [
48173         "Sweden (Sverige)",
48174         "se",
48175         "46"
48176       ],
48177       [
48178         "Switzerland (Schweiz)",
48179         "ch",
48180         "41"
48181       ],
48182       [
48183         "Syria (‫سوريا‬‎)",
48184         "sy",
48185         "963"
48186       ],
48187       [
48188         "Taiwan (台灣)",
48189         "tw",
48190         "886"
48191       ],
48192       [
48193         "Tajikistan",
48194         "tj",
48195         "992"
48196       ],
48197       [
48198         "Tanzania",
48199         "tz",
48200         "255"
48201       ],
48202       [
48203         "Thailand (ไทย)",
48204         "th",
48205         "66"
48206       ],
48207       [
48208         "Timor-Leste",
48209         "tl",
48210         "670"
48211       ],
48212       [
48213         "Togo",
48214         "tg",
48215         "228"
48216       ],
48217       [
48218         "Tokelau",
48219         "tk",
48220         "690"
48221       ],
48222       [
48223         "Tonga",
48224         "to",
48225         "676"
48226       ],
48227       [
48228         "Trinidad and Tobago",
48229         "tt",
48230         "1868"
48231       ],
48232       [
48233         "Tunisia (‫تونس‬‎)",
48234         "tn",
48235         "216"
48236       ],
48237       [
48238         "Turkey (Türkiye)",
48239         "tr",
48240         "90"
48241       ],
48242       [
48243         "Turkmenistan",
48244         "tm",
48245         "993"
48246       ],
48247       [
48248         "Turks and Caicos Islands",
48249         "tc",
48250         "1649"
48251       ],
48252       [
48253         "Tuvalu",
48254         "tv",
48255         "688"
48256       ],
48257       [
48258         "U.S. Virgin Islands",
48259         "vi",
48260         "1340"
48261       ],
48262       [
48263         "Uganda",
48264         "ug",
48265         "256"
48266       ],
48267       [
48268         "Ukraine (Україна)",
48269         "ua",
48270         "380"
48271       ],
48272       [
48273         "United Arab Emirates (‫الإمارات العربية المتحدة‬‎)",
48274         "ae",
48275         "971"
48276       ],
48277       [
48278         "United Kingdom",
48279         "gb",
48280         "44",
48281         0
48282       ],
48283       [
48284         "United States",
48285         "us",
48286         "1",
48287         0
48288       ],
48289       [
48290         "Uruguay",
48291         "uy",
48292         "598"
48293       ],
48294       [
48295         "Uzbekistan (Oʻzbekiston)",
48296         "uz",
48297         "998"
48298       ],
48299       [
48300         "Vanuatu",
48301         "vu",
48302         "678"
48303       ],
48304       [
48305         "Vatican City (Città del Vaticano)",
48306         "va",
48307         "39",
48308         1
48309       ],
48310       [
48311         "Venezuela",
48312         "ve",
48313         "58"
48314       ],
48315       [
48316         "Vietnam (Việt Nam)",
48317         "vn",
48318         "84"
48319       ],
48320       [
48321         "Wallis and Futuna (Wallis-et-Futuna)",
48322         "wf",
48323         "681"
48324       ],
48325       [
48326         "Western Sahara (‫الصحراء الغربية‬‎)",
48327         "eh",
48328         "212",
48329         1
48330       ],
48331       [
48332         "Yemen (‫اليمن‬‎)",
48333         "ye",
48334         "967"
48335       ],
48336       [
48337         "Zambia",
48338         "zm",
48339         "260"
48340       ],
48341       [
48342         "Zimbabwe",
48343         "zw",
48344         "263"
48345       ],
48346       [
48347         "Åland Islands",
48348         "ax",
48349         "358",
48350         1
48351       ]
48352   ];
48353   
48354   return d;
48355 }/**
48356 *    This script refer to:
48357 *    Title: International Telephone Input
48358 *    Author: Jack O'Connor
48359 *    Code version:  v12.1.12
48360 *    Availability: https://github.com/jackocnr/intl-tel-input.git
48361 **/
48362
48363 /**
48364  * @class Roo.bootstrap.form.PhoneInput
48365  * @extends Roo.bootstrap.form.TriggerField
48366  * An input with International dial-code selection
48367  
48368  * @cfg {String} defaultDialCode default '+852'
48369  * @cfg {Array} preferedCountries default []
48370   
48371  * @constructor
48372  * Create a new PhoneInput.
48373  * @param {Object} config Configuration options
48374  */
48375
48376 Roo.bootstrap.form.PhoneInput = function(config) {
48377     Roo.bootstrap.form.PhoneInput.superclass.constructor.call(this, config);
48378 };
48379
48380 Roo.extend(Roo.bootstrap.form.PhoneInput, Roo.bootstrap.form.TriggerField, {
48381         /**
48382         * @cfg {Roo.data.Store} store [required] The data store to which this combo is bound (defaults to undefined)
48383         */
48384         listWidth: undefined,
48385         
48386         selectedClass: 'active',
48387         
48388         invalidClass : "has-warning",
48389         
48390         validClass: 'has-success',
48391         
48392         allowed: '0123456789',
48393         
48394         max_length: 15,
48395         
48396         /**
48397          * @cfg {String} defaultDialCode The default dial code when initializing the input
48398          */
48399         defaultDialCode: '+852',
48400         
48401         /**
48402          * @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
48403          */
48404         preferedCountries: false,
48405         
48406         getAutoCreate : function()
48407         {
48408             var data = Roo.bootstrap.form.PhoneInputData();
48409             var align = this.labelAlign || this.parentLabelAlign();
48410             var id = Roo.id();
48411             
48412             this.allCountries = [];
48413             this.dialCodeMapping = [];
48414             
48415             for (var i = 0; i < data.length; i++) {
48416               var c = data[i];
48417               this.allCountries[i] = {
48418                 name: c[0],
48419                 iso2: c[1],
48420                 dialCode: c[2],
48421                 priority: c[3] || 0,
48422                 areaCodes: c[4] || null
48423               };
48424               this.dialCodeMapping[c[2]] = {
48425                   name: c[0],
48426                   iso2: c[1],
48427                   priority: c[3] || 0,
48428                   areaCodes: c[4] || null
48429               };
48430             }
48431             
48432             var cfg = {
48433                 cls: 'form-group',
48434                 cn: []
48435             };
48436             
48437             var input =  {
48438                 tag: 'input',
48439                 id : id,
48440                 // type: 'number', -- do not use number - we get the flaky up/down arrows.
48441                 maxlength: this.max_length,
48442                 cls : 'form-control tel-input',
48443                 autocomplete: 'new-password'
48444             };
48445             
48446             var hiddenInput = {
48447                 tag: 'input',
48448                 type: 'hidden',
48449                 cls: 'hidden-tel-input'
48450             };
48451             
48452             if (this.name) {
48453                 hiddenInput.name = this.name;
48454             }
48455             
48456             if (this.disabled) {
48457                 input.disabled = true;
48458             }
48459             
48460             var flag_container = {
48461                 tag: 'div',
48462                 cls: 'flag-box',
48463                 cn: [
48464                     {
48465                         tag: 'div',
48466                         cls: 'flag'
48467                     },
48468                     {
48469                         tag: 'div',
48470                         cls: 'caret'
48471                     }
48472                 ]
48473             };
48474             
48475             var box = {
48476                 tag: 'div',
48477                 cls: this.hasFeedback ? 'has-feedback' : '',
48478                 cn: [
48479                     hiddenInput,
48480                     input,
48481                     {
48482                         tag: 'input',
48483                         cls: 'dial-code-holder',
48484                         disabled: true
48485                     }
48486                 ]
48487             };
48488             
48489             var container = {
48490                 cls: 'roo-select2-container input-group',
48491                 cn: [
48492                     flag_container,
48493                     box
48494                 ]
48495             };
48496             
48497             if (this.fieldLabel.length) {
48498                 var indicator = {
48499                     tag: 'i',
48500                     tooltip: 'This field is required'
48501                 };
48502                 
48503                 var label = {
48504                     tag: 'label',
48505                     'for':  id,
48506                     cls: 'control-label',
48507                     cn: []
48508                 };
48509                 
48510                 var label_text = {
48511                     tag: 'span',
48512                     html: this.fieldLabel
48513                 };
48514                 
48515                 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star left-indicator';
48516                 label.cn = [
48517                     indicator,
48518                     label_text
48519                 ];
48520                 
48521                 if(this.indicatorpos == 'right') {
48522                     indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star right-indicator';
48523                     label.cn = [
48524                         label_text,
48525                         indicator
48526                     ];
48527                 }
48528                 
48529                 if(align == 'left') {
48530                     container = {
48531                         tag: 'div',
48532                         cn: [
48533                             container
48534                         ]
48535                     };
48536                     
48537                     if(this.labelWidth > 12){
48538                         label.style = "width: " + this.labelWidth + 'px';
48539                     }
48540                     if(this.labelWidth < 13 && this.labelmd == 0){
48541                         this.labelmd = this.labelWidth;
48542                     }
48543                     if(this.labellg > 0){
48544                         label.cls += ' col-lg-' + this.labellg;
48545                         input.cls += ' col-lg-' + (12 - this.labellg);
48546                     }
48547                     if(this.labelmd > 0){
48548                         label.cls += ' col-md-' + this.labelmd;
48549                         container.cls += ' col-md-' + (12 - this.labelmd);
48550                     }
48551                     if(this.labelsm > 0){
48552                         label.cls += ' col-sm-' + this.labelsm;
48553                         container.cls += ' col-sm-' + (12 - this.labelsm);
48554                     }
48555                     if(this.labelxs > 0){
48556                         label.cls += ' col-xs-' + this.labelxs;
48557                         container.cls += ' col-xs-' + (12 - this.labelxs);
48558                     }
48559                 }
48560             }
48561             
48562             cfg.cn = [
48563                 label,
48564                 container
48565             ];
48566             
48567             var settings = this;
48568             
48569             ['xs','sm','md','lg'].map(function(size){
48570                 if (settings[size]) {
48571                     cfg.cls += ' col-' + size + '-' + settings[size];
48572                 }
48573             });
48574             
48575             this.store = new Roo.data.Store({
48576                 proxy : new Roo.data.MemoryProxy({}),
48577                 reader : new Roo.data.JsonReader({
48578                     fields : [
48579                         {
48580                             'name' : 'name',
48581                             'type' : 'string'
48582                         },
48583                         {
48584                             'name' : 'iso2',
48585                             'type' : 'string'
48586                         },
48587                         {
48588                             'name' : 'dialCode',
48589                             'type' : 'string'
48590                         },
48591                         {
48592                             'name' : 'priority',
48593                             'type' : 'string'
48594                         },
48595                         {
48596                             'name' : 'areaCodes',
48597                             'type' : 'string'
48598                         }
48599                     ]
48600                 })
48601             });
48602             
48603             if(!this.preferedCountries) {
48604                 this.preferedCountries = [
48605                     'hk',
48606                     'gb',
48607                     'us'
48608                 ];
48609             }
48610             
48611             var p = this.preferedCountries.reverse();
48612             
48613             if(p) {
48614                 for (var i = 0; i < p.length; i++) {
48615                     for (var j = 0; j < this.allCountries.length; j++) {
48616                         if(this.allCountries[j].iso2 == p[i]) {
48617                             var t = this.allCountries[j];
48618                             this.allCountries.splice(j,1);
48619                             this.allCountries.unshift(t);
48620                         }
48621                     } 
48622                 }
48623             }
48624             
48625             this.store.proxy.data = {
48626                 success: true,
48627                 data: this.allCountries
48628             };
48629             
48630             return cfg;
48631         },
48632         
48633         initEvents : function()
48634         {
48635             this.createList();
48636             Roo.bootstrap.form.PhoneInput.superclass.initEvents.call(this);
48637             
48638             this.indicator = this.indicatorEl();
48639             this.flag = this.flagEl();
48640             this.dialCodeHolder = this.dialCodeHolderEl();
48641             
48642             this.trigger = this.el.select('div.flag-box',true).first();
48643             this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
48644             
48645             var _this = this;
48646             
48647             (function(){
48648                 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
48649                 _this.list.setWidth(lw);
48650             }).defer(100);
48651             
48652             this.list.on('mouseover', this.onViewOver, this);
48653             this.list.on('mousemove', this.onViewMove, this);
48654             this.inputEl().on("keyup", this.onKeyUp, this);
48655             this.inputEl().on("keypress", this.onKeyPress, this);
48656             
48657             this.tpl = '<li><a href="#"><div class="flag {iso2}"></div>{name} <span class="dial-code">+{dialCode}</span></a></li>';
48658
48659             this.view = new Roo.View(this.list, this.tpl, {
48660                 singleSelect:true, store: this.store, selectedClass: this.selectedClass
48661             });
48662             
48663             this.view.on('click', this.onViewClick, this);
48664             this.setValue(this.defaultDialCode);
48665         },
48666         
48667         onTriggerClick : function(e)
48668         {
48669             Roo.log('trigger click');
48670             if(this.disabled){
48671                 return;
48672             }
48673             
48674             if(this.isExpanded()){
48675                 this.collapse();
48676                 this.hasFocus = false;
48677             }else {
48678                 this.store.load({});
48679                 this.hasFocus = true;
48680                 this.expand();
48681             }
48682         },
48683         
48684         isExpanded : function()
48685         {
48686             return this.list.isVisible();
48687         },
48688         
48689         collapse : function()
48690         {
48691             if(!this.isExpanded()){
48692                 return;
48693             }
48694             this.list.hide();
48695             Roo.get(document).un('mousedown', this.collapseIf, this);
48696             Roo.get(document).un('mousewheel', this.collapseIf, this);
48697             this.fireEvent('collapse', this);
48698             this.validate();
48699         },
48700         
48701         expand : function()
48702         {
48703             Roo.log('expand');
48704
48705             if(this.isExpanded() || !this.hasFocus){
48706                 return;
48707             }
48708             
48709             var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
48710             this.list.setWidth(lw);
48711             
48712             this.list.show();
48713             this.restrictHeight();
48714             
48715             Roo.get(document).on('mousedown', this.collapseIf, this);
48716             Roo.get(document).on('mousewheel', this.collapseIf, this);
48717             
48718             this.fireEvent('expand', this);
48719         },
48720         
48721         restrictHeight : function()
48722         {
48723             this.list.alignTo(this.inputEl(), this.listAlign);
48724             this.list.alignTo(this.inputEl(), this.listAlign);
48725         },
48726         
48727         onViewOver : function(e, t)
48728         {
48729             if(this.inKeyMode){
48730                 return;
48731             }
48732             var item = this.view.findItemFromChild(t);
48733             
48734             if(item){
48735                 var index = this.view.indexOf(item);
48736                 this.select(index, false);
48737             }
48738         },
48739
48740         // private
48741         onViewClick : function(view, doFocus, el, e)
48742         {
48743             var index = this.view.getSelectedIndexes()[0];
48744             
48745             var r = this.store.getAt(index);
48746             
48747             if(r){
48748                 this.onSelect(r, index);
48749             }
48750             if(doFocus !== false && !this.blockFocus){
48751                 this.inputEl().focus();
48752             }
48753         },
48754         
48755         onViewMove : function(e, t)
48756         {
48757             this.inKeyMode = false;
48758         },
48759         
48760         select : function(index, scrollIntoView)
48761         {
48762             this.selectedIndex = index;
48763             this.view.select(index);
48764             if(scrollIntoView !== false){
48765                 var el = this.view.getNode(index);
48766                 if(el){
48767                     this.list.scrollChildIntoView(el, false);
48768                 }
48769             }
48770         },
48771         
48772         createList : function()
48773         {
48774             this.list = Roo.get(document.body).createChild({
48775                 tag: 'ul',
48776                 cls: 'typeahead typeahead-long dropdown-menu tel-list',
48777                 style: 'display:none'
48778             });
48779             
48780             this.list.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
48781         },
48782         
48783         collapseIf : function(e)
48784         {
48785             var in_combo  = e.within(this.el);
48786             var in_list =  e.within(this.list);
48787             var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
48788             
48789             if (in_combo || in_list || is_list) {
48790                 return;
48791             }
48792             this.collapse();
48793         },
48794         
48795         onSelect : function(record, index)
48796         {
48797             if(this.fireEvent('beforeselect', this, record, index) !== false){
48798                 
48799                 this.setFlagClass(record.data.iso2);
48800                 this.setDialCode(record.data.dialCode);
48801                 this.hasFocus = false;
48802                 this.collapse();
48803                 this.fireEvent('select', this, record, index);
48804             }
48805         },
48806         
48807         flagEl : function()
48808         {
48809             var flag = this.el.select('div.flag',true).first();
48810             if(!flag){
48811                 return false;
48812             }
48813             return flag;
48814         },
48815         
48816         dialCodeHolderEl : function()
48817         {
48818             var d = this.el.select('input.dial-code-holder',true).first();
48819             if(!d){
48820                 return false;
48821             }
48822             return d;
48823         },
48824         
48825         setDialCode : function(v)
48826         {
48827             this.dialCodeHolder.dom.value = '+'+v;
48828         },
48829         
48830         setFlagClass : function(n)
48831         {
48832             this.flag.dom.className = 'flag '+n;
48833         },
48834         
48835         getValue : function()
48836         {
48837             var v = this.inputEl().getValue();
48838             if(this.dialCodeHolder) {
48839                 v = this.dialCodeHolder.dom.value+this.inputEl().getValue();
48840             }
48841             return v;
48842         },
48843         
48844         setValue : function(v)
48845         {
48846             var d = this.getDialCode(v);
48847             
48848             //invalid dial code
48849             if(v.length == 0 || !d || d.length == 0) {
48850                 if(this.rendered){
48851                     this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
48852                     this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
48853                 }
48854                 return;
48855             }
48856             
48857             //valid dial code
48858             this.setFlagClass(this.dialCodeMapping[d].iso2);
48859             this.setDialCode(d);
48860             this.inputEl().dom.value = v.replace('+'+d,'');
48861             this.hiddenEl().dom.value = this.getValue();
48862             
48863             this.validate();
48864         },
48865         
48866         getDialCode : function(v)
48867         {
48868             v = v ||  '';
48869             
48870             if (v.length == 0) {
48871                 return this.dialCodeHolder.dom.value;
48872             }
48873             
48874             var dialCode = "";
48875             if (v.charAt(0) != "+") {
48876                 return false;
48877             }
48878             var numericChars = "";
48879             for (var i = 1; i < v.length; i++) {
48880               var c = v.charAt(i);
48881               if (!isNaN(c)) {
48882                 numericChars += c;
48883                 if (this.dialCodeMapping[numericChars]) {
48884                   dialCode = v.substr(1, i);
48885                 }
48886                 if (numericChars.length == 4) {
48887                   break;
48888                 }
48889               }
48890             }
48891             return dialCode;
48892         },
48893         
48894         reset : function()
48895         {
48896             this.setValue(this.defaultDialCode);
48897             this.validate();
48898         },
48899         
48900         hiddenEl : function()
48901         {
48902             return this.el.select('input.hidden-tel-input',true).first();
48903         },
48904         
48905         // after setting val
48906         onKeyUp : function(e){
48907             this.setValue(this.getValue());
48908         },
48909         
48910         onKeyPress : function(e){
48911             if(this.allowed.indexOf(String.fromCharCode(e.getCharCode())) === -1){
48912                 e.stopEvent();
48913             }
48914         }
48915         
48916 });
48917 /**
48918  * @class Roo.bootstrap.form.MoneyField
48919  * @extends Roo.bootstrap.form.ComboBox
48920  * Bootstrap MoneyField class
48921  * 
48922  * @constructor
48923  * Create a new MoneyField.
48924  * @param {Object} config Configuration options
48925  */
48926
48927 Roo.bootstrap.form.MoneyField = function(config) {
48928     
48929     Roo.bootstrap.form.MoneyField.superclass.constructor.call(this, config);
48930     
48931 };
48932
48933 Roo.extend(Roo.bootstrap.form.MoneyField, Roo.bootstrap.form.ComboBox, {
48934     
48935     /**
48936      * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
48937      */
48938     allowDecimals : true,
48939     /**
48940      * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
48941      */
48942     decimalSeparator : ".",
48943     /**
48944      * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
48945      */
48946     decimalPrecision : 0,
48947     /**
48948      * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
48949      */
48950     allowNegative : true,
48951     /**
48952      * @cfg {Boolean} allowZero False to blank out if the user enters '0' (defaults to true)
48953      */
48954     allowZero: true,
48955     /**
48956      * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
48957      */
48958     minValue : Number.NEGATIVE_INFINITY,
48959     /**
48960      * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
48961      */
48962     maxValue : Number.MAX_VALUE,
48963     /**
48964      * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
48965      */
48966     minText : "The minimum value for this field is {0}",
48967     /**
48968      * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
48969      */
48970     maxText : "The maximum value for this field is {0}",
48971     /**
48972      * @cfg {String} nanText Error text to display if the value is not a valid number.  For example, this can happen
48973      * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
48974      */
48975     nanText : "{0} is not a valid number",
48976     /**
48977      * @cfg {Boolean} castInt (true|false) cast int if true (defalut true)
48978      */
48979     castInt : true,
48980     /**
48981      * @cfg {String} defaults currency of the MoneyField
48982      * value should be in lkey
48983      */
48984     defaultCurrency : false,
48985     /**
48986      * @cfg {String} thousandsDelimiter Symbol of thousandsDelimiter
48987      */
48988     thousandsDelimiter : false,
48989     /**
48990      * @cfg {Number} max_length Maximum input field length allowed (defaults to Number.MAX_VALUE)
48991      */
48992     max_length: false,
48993     
48994     inputlg : 9,
48995     inputmd : 9,
48996     inputsm : 9,
48997     inputxs : 6,
48998      /**
48999      * @cfg {Roo.data.Store} store  Store to lookup currency??
49000      */
49001     store : false,
49002     
49003     getAutoCreate : function()
49004     {
49005         var align = this.labelAlign || this.parentLabelAlign();
49006         
49007         var id = Roo.id();
49008
49009         var cfg = {
49010             cls: 'form-group',
49011             cn: []
49012         };
49013
49014         var input =  {
49015             tag: 'input',
49016             id : id,
49017             cls : 'form-control roo-money-amount-input',
49018             autocomplete: 'new-password'
49019         };
49020         
49021         var hiddenInput = {
49022             tag: 'input',
49023             type: 'hidden',
49024             id: Roo.id(),
49025             cls: 'hidden-number-input'
49026         };
49027         
49028         if(this.max_length) {
49029             input.maxlength = this.max_length; 
49030         }
49031         
49032         if (this.name) {
49033             hiddenInput.name = this.name;
49034         }
49035
49036         if (this.disabled) {
49037             input.disabled = true;
49038         }
49039
49040         var clg = 12 - this.inputlg;
49041         var cmd = 12 - this.inputmd;
49042         var csm = 12 - this.inputsm;
49043         var cxs = 12 - this.inputxs;
49044         
49045         var container = {
49046             tag : 'div',
49047             cls : 'row roo-money-field',
49048             cn : [
49049                 {
49050                     tag : 'div',
49051                     cls : 'roo-money-currency column col-lg-' + clg + ' col-md-' + cmd + ' col-sm-' + csm + ' col-xs-' + cxs,
49052                     cn : [
49053                         {
49054                             tag : 'div',
49055                             cls: 'roo-select2-container input-group',
49056                             cn: [
49057                                 {
49058                                     tag : 'input',
49059                                     cls : 'form-control roo-money-currency-input',
49060                                     autocomplete: 'new-password',
49061                                     readOnly : 1,
49062                                     name : this.currencyName
49063                                 },
49064                                 {
49065                                     tag :'span',
49066                                     cls : 'input-group-addon',
49067                                     cn : [
49068                                         {
49069                                             tag: 'span',
49070                                             cls: 'caret'
49071                                         }
49072                                     ]
49073                                 }
49074                             ]
49075                         }
49076                     ]
49077                 },
49078                 {
49079                     tag : 'div',
49080                     cls : 'roo-money-amount column col-lg-' + this.inputlg + ' col-md-' + this.inputmd + ' col-sm-' + this.inputsm + ' col-xs-' + this.inputxs,
49081                     cn : [
49082                         {
49083                             tag: 'div',
49084                             cls: this.hasFeedback ? 'has-feedback' : '',
49085                             cn: [
49086                                 input
49087                             ]
49088                         }
49089                     ]
49090                 }
49091             ]
49092             
49093         };
49094         
49095         if (this.fieldLabel.length) {
49096             var indicator = {
49097                 tag: 'i',
49098                 tooltip: 'This field is required'
49099             };
49100
49101             var label = {
49102                 tag: 'label',
49103                 'for':  id,
49104                 cls: 'control-label',
49105                 cn: []
49106             };
49107
49108             var label_text = {
49109                 tag: 'span',
49110                 html: this.fieldLabel
49111             };
49112
49113             indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star left-indicator';
49114             label.cn = [
49115                 indicator,
49116                 label_text
49117             ];
49118
49119             if(this.indicatorpos == 'right') {
49120                 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star right-indicator';
49121                 label.cn = [
49122                     label_text,
49123                     indicator
49124                 ];
49125             }
49126
49127             if(align == 'left') {
49128                 container = {
49129                     tag: 'div',
49130                     cn: [
49131                         container
49132                     ]
49133                 };
49134
49135                 if(this.labelWidth > 12){
49136                     label.style = "width: " + this.labelWidth + 'px';
49137                 }
49138                 if(this.labelWidth < 13 && this.labelmd == 0){
49139                     this.labelmd = this.labelWidth;
49140                 }
49141                 if(this.labellg > 0){
49142                     label.cls += ' col-lg-' + this.labellg;
49143                     input.cls += ' col-lg-' + (12 - this.labellg);
49144                 }
49145                 if(this.labelmd > 0){
49146                     label.cls += ' col-md-' + this.labelmd;
49147                     container.cls += ' col-md-' + (12 - this.labelmd);
49148                 }
49149                 if(this.labelsm > 0){
49150                     label.cls += ' col-sm-' + this.labelsm;
49151                     container.cls += ' col-sm-' + (12 - this.labelsm);
49152                 }
49153                 if(this.labelxs > 0){
49154                     label.cls += ' col-xs-' + this.labelxs;
49155                     container.cls += ' col-xs-' + (12 - this.labelxs);
49156                 }
49157             }
49158         }
49159
49160         cfg.cn = [
49161             label,
49162             container,
49163             hiddenInput
49164         ];
49165         
49166         var settings = this;
49167
49168         ['xs','sm','md','lg'].map(function(size){
49169             if (settings[size]) {
49170                 cfg.cls += ' col-' + size + '-' + settings[size];
49171             }
49172         });
49173         
49174         return cfg;
49175     },
49176     
49177     initEvents : function()
49178     {
49179         this.indicator = this.indicatorEl();
49180         
49181         this.initCurrencyEvent();
49182         
49183         this.initNumberEvent();
49184     },
49185     
49186     initCurrencyEvent : function()
49187     {
49188         if (!this.store) {
49189             throw "can not find store for combo";
49190         }
49191         
49192         this.store = Roo.factory(this.store, Roo.data);
49193         this.store.parent = this;
49194         
49195         this.createList();
49196         
49197         this.triggerEl = this.el.select('.input-group-addon', true).first();
49198         
49199         this.triggerEl.on("click", this.onTriggerClick, this, { preventDefault : true });
49200         
49201         var _this = this;
49202         
49203         (function(){
49204             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
49205             _this.list.setWidth(lw);
49206         }).defer(100);
49207         
49208         this.list.on('mouseover', this.onViewOver, this);
49209         this.list.on('mousemove', this.onViewMove, this);
49210         this.list.on('scroll', this.onViewScroll, this);
49211         
49212         if(!this.tpl){
49213             this.tpl = '<li><a href="#">{' + this.currencyField + '}</a></li>';
49214         }
49215         
49216         this.view = new Roo.View(this.list, this.tpl, {
49217             singleSelect:true, store: this.store, selectedClass: this.selectedClass
49218         });
49219         
49220         this.view.on('click', this.onViewClick, this);
49221         
49222         this.store.on('beforeload', this.onBeforeLoad, this);
49223         this.store.on('load', this.onLoad, this);
49224         this.store.on('loadexception', this.onLoadException, this);
49225         
49226         this.keyNav = new Roo.KeyNav(this.currencyEl(), {
49227             "up" : function(e){
49228                 this.inKeyMode = true;
49229                 this.selectPrev();
49230             },
49231
49232             "down" : function(e){
49233                 if(!this.isExpanded()){
49234                     this.onTriggerClick();
49235                 }else{
49236                     this.inKeyMode = true;
49237                     this.selectNext();
49238                 }
49239             },
49240
49241             "enter" : function(e){
49242                 this.collapse();
49243                 
49244                 if(this.fireEvent("specialkey", this, e)){
49245                     this.onViewClick(false);
49246                 }
49247                 
49248                 return true;
49249             },
49250
49251             "esc" : function(e){
49252                 this.collapse();
49253             },
49254
49255             "tab" : function(e){
49256                 this.collapse();
49257                 
49258                 if(this.fireEvent("specialkey", this, e)){
49259                     this.onViewClick(false);
49260                 }
49261                 
49262                 return true;
49263             },
49264
49265             scope : this,
49266
49267             doRelay : function(foo, bar, hname){
49268                 if(hname == 'down' || this.scope.isExpanded()){
49269                    return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
49270                 }
49271                 return true;
49272             },
49273
49274             forceKeyDown: true
49275         });
49276         
49277         this.currencyEl().on("click", this.onTriggerClick, this, { preventDefault : true });
49278         
49279     },
49280     
49281     initNumberEvent : function(e)
49282     {
49283         this.inputEl().on("keydown" , this.fireKey,  this);
49284         this.inputEl().on("focus", this.onFocus,  this);
49285         this.inputEl().on("blur", this.onBlur,  this);
49286         
49287         this.inputEl().relayEvent('keyup', this);
49288         
49289         if(this.indicator){
49290             this.indicator.addClass('invisible');
49291         }
49292  
49293         this.originalValue = this.getValue();
49294         
49295         if(this.validationEvent == 'keyup'){
49296             this.validationTask = new Roo.util.DelayedTask(this.validate, this);
49297             this.inputEl().on('keyup', this.filterValidation, this);
49298         }
49299         else if(this.validationEvent !== false){
49300             this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
49301         }
49302         
49303         if(this.selectOnFocus){
49304             this.on("focus", this.preFocus, this);
49305             
49306         }
49307         if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
49308             this.inputEl().on("keypress", this.filterKeys, this);
49309         } else {
49310             this.inputEl().relayEvent('keypress', this);
49311         }
49312         
49313         var allowed = "0123456789";
49314         
49315         if(this.allowDecimals){
49316             allowed += this.decimalSeparator;
49317         }
49318         
49319         if(this.allowNegative){
49320             allowed += "-";
49321         }
49322         
49323         if(this.thousandsDelimiter) {
49324             allowed += ",";
49325         }
49326         
49327         this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
49328         
49329         var keyPress = function(e){
49330             
49331             var k = e.getKey();
49332             
49333             var c = e.getCharCode();
49334             
49335             if(
49336                     (String.fromCharCode(c) == '.' || String.fromCharCode(c) == '-') &&
49337                     allowed.indexOf(String.fromCharCode(c)) === -1
49338             ){
49339                 e.stopEvent();
49340                 return;
49341             }
49342             
49343             if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
49344                 return;
49345             }
49346             
49347             if(allowed.indexOf(String.fromCharCode(c)) === -1){
49348                 e.stopEvent();
49349             }
49350         };
49351         
49352         this.inputEl().on("keypress", keyPress, this);
49353         
49354     },
49355     
49356     onTriggerClick : function(e)
49357     {   
49358         if(this.disabled){
49359             return;
49360         }
49361         
49362         this.page = 0;
49363         this.loadNext = false;
49364         
49365         if(this.isExpanded()){
49366             this.collapse();
49367             return;
49368         }
49369         
49370         this.hasFocus = true;
49371         
49372         if(this.triggerAction == 'all') {
49373             this.doQuery(this.allQuery, true);
49374             return;
49375         }
49376         
49377         this.doQuery(this.getRawValue());
49378     },
49379     
49380     getCurrency : function()
49381     {   
49382         var v = this.currencyEl().getValue();
49383         
49384         return v;
49385     },
49386     
49387     restrictHeight : function()
49388     {
49389         this.list.alignTo(this.currencyEl(), this.listAlign);
49390         this.list.alignTo(this.currencyEl(), this.listAlign);
49391     },
49392     
49393     onViewClick : function(view, doFocus, el, e)
49394     {
49395         var index = this.view.getSelectedIndexes()[0];
49396         
49397         var r = this.store.getAt(index);
49398         
49399         if(r){
49400             this.onSelect(r, index);
49401         }
49402     },
49403     
49404     onSelect : function(record, index){
49405         
49406         if(this.fireEvent('beforeselect', this, record, index) !== false){
49407         
49408             this.setFromCurrencyData(index > -1 ? record.data : false);
49409             
49410             this.collapse();
49411             
49412             this.fireEvent('select', this, record, index);
49413         }
49414     },
49415     
49416     setFromCurrencyData : function(o)
49417     {
49418         var currency = '';
49419         
49420         this.lastCurrency = o;
49421         
49422         if (this.currencyField) {
49423             currency = !o || typeof(o[this.currencyField]) == 'undefined' ? '' : o[this.currencyField];
49424         } else {
49425             Roo.log('no  currencyField value set for '+ (this.name ? this.name : this.id));
49426         }
49427         
49428         this.lastSelectionText = currency;
49429         
49430         //setting default currency
49431         if(o[this.currencyField] * 1 == 0 && this.defaultCurrency) {
49432             this.setCurrency(this.defaultCurrency);
49433             return;
49434         }
49435         
49436         this.setCurrency(currency);
49437     },
49438     
49439     setFromData : function(o)
49440     {
49441         var c = {};
49442         
49443         c[this.currencyField] = !o || typeof(o[this.currencyName]) == 'undefined' ? '' : o[this.currencyName];
49444         
49445         this.setFromCurrencyData(c);
49446         
49447         var value = '';
49448         
49449         if (this.name) {
49450             value = !o || typeof(o[this.name]) == 'undefined' ? '' : o[this.name];
49451         } else {
49452             Roo.log('no value set for '+ (this.name ? this.name : this.id));
49453         }
49454         
49455         this.setValue(value);
49456         
49457     },
49458     
49459     setCurrency : function(v)
49460     {   
49461         this.currencyValue = v;
49462         
49463         if(this.rendered){
49464             this.currencyEl().dom.value = (v === null || v === undefined ? '' : v);
49465             this.validate();
49466         }
49467     },
49468     
49469     setValue : function(v)
49470     {
49471         v = String(this.fixPrecision(v)).replace(".", this.decimalSeparator);
49472         
49473         this.value = v;
49474         
49475         if(this.rendered){
49476             
49477             this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
49478             
49479             this.inputEl().dom.value = (v == '') ? '' :
49480                 Roo.util.Format.number(v, this.decimalPrecision, this.thousandsDelimiter || '');
49481             
49482             if(!this.allowZero && v === '0') {
49483                 this.hiddenEl().dom.value = '';
49484                 this.inputEl().dom.value = '';
49485             }
49486             
49487             this.validate();
49488         }
49489     },
49490     
49491     getRawValue : function()
49492     {
49493         var v = this.inputEl().getValue();
49494         
49495         return v;
49496     },
49497     
49498     getValue : function()
49499     {
49500         return this.fixPrecision(this.parseValue(this.getRawValue()));
49501     },
49502     
49503     parseValue : function(value)
49504     {
49505         if(this.thousandsDelimiter) {
49506             value += "";
49507             r = new RegExp(",", "g");
49508             value = value.replace(r, "");
49509         }
49510         
49511         value = parseFloat(String(value).replace(this.decimalSeparator, "."));
49512         return isNaN(value) ? '' : value;
49513         
49514     },
49515     
49516     fixPrecision : function(value)
49517     {
49518         if(this.thousandsDelimiter) {
49519             value += "";
49520             r = new RegExp(",", "g");
49521             value = value.replace(r, "");
49522         }
49523         
49524         var nan = isNaN(value);
49525         
49526         if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
49527             return nan ? '' : value;
49528         }
49529         return parseFloat(value).toFixed(this.decimalPrecision);
49530     },
49531     
49532     decimalPrecisionFcn : function(v)
49533     {
49534         return Math.floor(v);
49535     },
49536     
49537     validateValue : function(value)
49538     {
49539         if(!Roo.bootstrap.form.MoneyField.superclass.validateValue.call(this, value)){
49540             return false;
49541         }
49542         
49543         var num = this.parseValue(value);
49544         
49545         if(isNaN(num)){
49546             this.markInvalid(String.format(this.nanText, value));
49547             return false;
49548         }
49549         
49550         if(num < this.minValue){
49551             this.markInvalid(String.format(this.minText, this.minValue));
49552             return false;
49553         }
49554         
49555         if(num > this.maxValue){
49556             this.markInvalid(String.format(this.maxText, this.maxValue));
49557             return false;
49558         }
49559         
49560         return true;
49561     },
49562     
49563     validate : function()
49564     {
49565         if(this.disabled || this.allowBlank){
49566             this.markValid();
49567             return true;
49568         }
49569         
49570         var currency = this.getCurrency();
49571         
49572         if(this.validateValue(this.getRawValue()) && currency.length){
49573             this.markValid();
49574             return true;
49575         }
49576         
49577         this.markInvalid();
49578         return false;
49579     },
49580     
49581     getName: function()
49582     {
49583         return this.name;
49584     },
49585     
49586     beforeBlur : function()
49587     {
49588         if(!this.castInt){
49589             return;
49590         }
49591         
49592         var v = this.parseValue(this.getRawValue());
49593         
49594         if(v || v == 0){
49595             this.setValue(v);
49596         }
49597     },
49598     
49599     onBlur : function()
49600     {
49601         this.beforeBlur();
49602         
49603         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
49604             //this.el.removeClass(this.focusClass);
49605         }
49606         
49607         this.hasFocus = false;
49608         
49609         if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
49610             this.validate();
49611         }
49612         
49613         var v = this.getValue();
49614         
49615         if(String(v) !== String(this.startValue)){
49616             this.fireEvent('change', this, v, this.startValue);
49617         }
49618         
49619         this.fireEvent("blur", this);
49620     },
49621     
49622     inputEl : function()
49623     {
49624         return this.el.select('.roo-money-amount-input', true).first();
49625     },
49626     
49627     currencyEl : function()
49628     {
49629         return this.el.select('.roo-money-currency-input', true).first();
49630     },
49631     
49632     hiddenEl : function()
49633     {
49634         return this.el.select('input.hidden-number-input',true).first();
49635     }
49636     
49637 });/**
49638  * @class Roo.bootstrap.BezierSignature
49639  * @extends Roo.bootstrap.Component
49640  * Bootstrap BezierSignature class
49641  * This script refer to:
49642  *    Title: Signature Pad
49643  *    Author: szimek
49644  *    Availability: https://github.com/szimek/signature_pad
49645  *
49646  * @constructor
49647  * Create a new BezierSignature
49648  * @param {Object} config The config object
49649  */
49650
49651 Roo.bootstrap.BezierSignature = function(config){
49652     Roo.bootstrap.BezierSignature.superclass.constructor.call(this, config);
49653     this.addEvents({
49654         "resize" : true
49655     });
49656 };
49657
49658 Roo.extend(Roo.bootstrap.BezierSignature, Roo.bootstrap.Component,
49659 {
49660      
49661     curve_data: [],
49662     
49663     is_empty: true,
49664     
49665     mouse_btn_down: true,
49666     
49667     /**
49668      * @cfg {int} canvas height
49669      */
49670     canvas_height: '200px',
49671     
49672     /**
49673      * @cfg {float|function} Radius of a single dot.
49674      */ 
49675     dot_size: false,
49676     
49677     /**
49678      * @cfg {float} Minimum width of a line. Defaults to 0.5.
49679      */
49680     min_width: 0.5,
49681     
49682     /**
49683      * @cfg {float} Maximum width of a line. Defaults to 2.5.
49684      */
49685     max_width: 2.5,
49686     
49687     /**
49688      * @cfg {integer} Draw the next point at most once per every x milliseconds. Set it to 0 to turn off throttling. Defaults to 16.
49689      */
49690     throttle: 16,
49691     
49692     /**
49693      * @cfg {integer} Add the next point only if the previous one is farther than x pixels. Defaults to 5.
49694      */
49695     min_distance: 5,
49696     
49697     /**
49698      * @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.
49699      */
49700     bg_color: 'rgba(0, 0, 0, 0)',
49701     
49702     /**
49703      * @cfg {string} Color used to draw the lines. Can be any color format accepted by context.fillStyle. Defaults to "black".
49704      */
49705     dot_color: 'black',
49706     
49707     /**
49708      * @cfg {float} Weight used to modify new velocity based on the previous velocity. Defaults to 0.7.
49709      */ 
49710     velocity_filter_weight: 0.7,
49711     
49712     /**
49713      * @cfg {function} Callback when stroke begin. 
49714      */
49715     onBegin: false,
49716     
49717     /**
49718      * @cfg {function} Callback when stroke end.
49719      */
49720     onEnd: false,
49721     
49722     getAutoCreate : function()
49723     {
49724         var cls = 'roo-signature column';
49725         
49726         if(this.cls){
49727             cls += ' ' + this.cls;
49728         }
49729         
49730         var col_sizes = [
49731             'lg',
49732             'md',
49733             'sm',
49734             'xs'
49735         ];
49736         
49737         for(var i = 0; i < col_sizes.length; i++) {
49738             if(this[col_sizes[i]]) {
49739                 cls += " col-"+col_sizes[i]+"-"+this[col_sizes[i]];
49740             }
49741         }
49742         
49743         var cfg = {
49744             tag: 'div',
49745             cls: cls,
49746             cn: [
49747                 {
49748                     tag: 'div',
49749                     cls: 'roo-signature-body',
49750                     cn: [
49751                         {
49752                             tag: 'canvas',
49753                             cls: 'roo-signature-body-canvas',
49754                             height: this.canvas_height,
49755                             width: this.canvas_width
49756                         }
49757                     ]
49758                 },
49759                 {
49760                     tag: 'input',
49761                     type: 'file',
49762                     style: 'display: none'
49763                 }
49764             ]
49765         };
49766         
49767         return cfg;
49768     },
49769     
49770     initEvents: function() 
49771     {
49772         Roo.bootstrap.BezierSignature.superclass.initEvents.call(this);
49773         
49774         var canvas = this.canvasEl();
49775         
49776         // mouse && touch event swapping...
49777         canvas.dom.style.touchAction = 'none';
49778         canvas.dom.style.msTouchAction = 'none';
49779         
49780         this.mouse_btn_down = false;
49781         canvas.on('mousedown', this._handleMouseDown, this);
49782         canvas.on('mousemove', this._handleMouseMove, this);
49783         Roo.select('html').first().on('mouseup', this._handleMouseUp, this);
49784         
49785         if (window.PointerEvent) {
49786             canvas.on('pointerdown', this._handleMouseDown, this);
49787             canvas.on('pointermove', this._handleMouseMove, this);
49788             Roo.select('html').first().on('pointerup', this._handleMouseUp, this);
49789         }
49790         
49791         if ('ontouchstart' in window) {
49792             canvas.on('touchstart', this._handleTouchStart, this);
49793             canvas.on('touchmove', this._handleTouchMove, this);
49794             canvas.on('touchend', this._handleTouchEnd, this);
49795         }
49796         
49797         Roo.EventManager.onWindowResize(this.resize, this, true);
49798         
49799         // file input event
49800         this.fileEl().on('change', this.uploadImage, this);
49801         
49802         this.clear();
49803         
49804         this.resize();
49805     },
49806     
49807     resize: function(){
49808         
49809         var canvas = this.canvasEl().dom;
49810         var ctx = this.canvasElCtx();
49811         var img_data = false;
49812         
49813         if(canvas.width > 0) {
49814             var img_data = ctx.getImageData(0, 0, canvas.width, canvas.height);
49815         }
49816         // setting canvas width will clean img data
49817         canvas.width = 0;
49818         
49819         var style = window.getComputedStyle ? 
49820             getComputedStyle(this.el.dom, null) : this.el.dom.currentStyle;
49821             
49822         var padding_left = parseInt(style.paddingLeft) || 0;
49823         var padding_right = parseInt(style.paddingRight) || 0;
49824         
49825         canvas.width = this.el.dom.clientWidth - padding_left - padding_right;
49826         
49827         if(img_data) {
49828             ctx.putImageData(img_data, 0, 0);
49829         }
49830     },
49831     
49832     _handleMouseDown: function(e)
49833     {
49834         if (e.browserEvent.which === 1) {
49835             this.mouse_btn_down = true;
49836             this.strokeBegin(e);
49837         }
49838     },
49839     
49840     _handleMouseMove: function (e)
49841     {
49842         if (this.mouse_btn_down) {
49843             this.strokeMoveUpdate(e);
49844         }
49845     },
49846     
49847     _handleMouseUp: function (e)
49848     {
49849         if (e.browserEvent.which === 1 && this.mouse_btn_down) {
49850             this.mouse_btn_down = false;
49851             this.strokeEnd(e);
49852         }
49853     },
49854     
49855     _handleTouchStart: function (e) {
49856         
49857         e.preventDefault();
49858         if (e.browserEvent.targetTouches.length === 1) {
49859             // var touch = e.browserEvent.changedTouches[0];
49860             // this.strokeBegin(touch);
49861             
49862              this.strokeBegin(e); // assume e catching the correct xy...
49863         }
49864     },
49865     
49866     _handleTouchMove: function (e) {
49867         e.preventDefault();
49868         // var touch = event.targetTouches[0];
49869         // _this._strokeMoveUpdate(touch);
49870         this.strokeMoveUpdate(e);
49871     },
49872     
49873     _handleTouchEnd: function (e) {
49874         var wasCanvasTouched = e.target === this.canvasEl().dom;
49875         if (wasCanvasTouched) {
49876             e.preventDefault();
49877             // var touch = event.changedTouches[0];
49878             // _this._strokeEnd(touch);
49879             this.strokeEnd(e);
49880         }
49881     },
49882     
49883     reset: function () {
49884         this._lastPoints = [];
49885         this._lastVelocity = 0;
49886         this._lastWidth = (this.min_width + this.max_width) / 2;
49887         this.canvasElCtx().fillStyle = this.dot_color;
49888     },
49889     
49890     strokeMoveUpdate: function(e)
49891     {
49892         this.strokeUpdate(e);
49893         
49894         if (this.throttle) {
49895             this.throttleStroke(this.strokeUpdate, this.throttle);
49896         }
49897         else {
49898             this.strokeUpdate(e);
49899         }
49900     },
49901     
49902     strokeBegin: function(e)
49903     {
49904         var newPointGroup = {
49905             color: this.dot_color,
49906             points: []
49907         };
49908         
49909         if (typeof this.onBegin === 'function') {
49910             this.onBegin(e);
49911         }
49912         
49913         this.curve_data.push(newPointGroup);
49914         this.reset();
49915         this.strokeUpdate(e);
49916     },
49917     
49918     strokeUpdate: function(e)
49919     {
49920         var rect = this.canvasEl().dom.getBoundingClientRect();
49921         var point = new this.Point(e.xy[0] - rect.left, e.xy[1] - rect.top, new Date().getTime());
49922         var lastPointGroup = this.curve_data[this.curve_data.length - 1];
49923         var lastPoints = lastPointGroup.points;
49924         var lastPoint = lastPoints.length > 0 && lastPoints[lastPoints.length - 1];
49925         var isLastPointTooClose = lastPoint
49926             ? point.distanceTo(lastPoint) <= this.min_distance
49927             : false;
49928         var color = lastPointGroup.color;
49929         if (!lastPoint || !(lastPoint && isLastPointTooClose)) {
49930             var curve = this.addPoint(point);
49931             if (!lastPoint) {
49932                 this.drawDot({color: color, point: point});
49933             }
49934             else if (curve) {
49935                 this.drawCurve({color: color, curve: curve});
49936             }
49937             lastPoints.push({
49938                 time: point.time,
49939                 x: point.x,
49940                 y: point.y
49941             });
49942         }
49943     },
49944     
49945     strokeEnd: function(e)
49946     {
49947         this.strokeUpdate(e);
49948         if (typeof this.onEnd === 'function') {
49949             this.onEnd(e);
49950         }
49951     },
49952     
49953     addPoint:  function (point) {
49954         var _lastPoints = this._lastPoints;
49955         _lastPoints.push(point);
49956         if (_lastPoints.length > 2) {
49957             if (_lastPoints.length === 3) {
49958                 _lastPoints.unshift(_lastPoints[0]);
49959             }
49960             var widths = this.calculateCurveWidths(_lastPoints[1], _lastPoints[2]);
49961             var curve = this.Bezier.fromPoints(_lastPoints, widths, this);
49962             _lastPoints.shift();
49963             return curve;
49964         }
49965         return null;
49966     },
49967     
49968     calculateCurveWidths: function (startPoint, endPoint) {
49969         var velocity = this.velocity_filter_weight * endPoint.velocityFrom(startPoint) +
49970             (1 - this.velocity_filter_weight) * this._lastVelocity;
49971
49972         var newWidth = Math.max(this.max_width / (velocity + 1), this.min_width);
49973         var widths = {
49974             end: newWidth,
49975             start: this._lastWidth
49976         };
49977         
49978         this._lastVelocity = velocity;
49979         this._lastWidth = newWidth;
49980         return widths;
49981     },
49982     
49983     drawDot: function (_a) {
49984         var color = _a.color, point = _a.point;
49985         var ctx = this.canvasElCtx();
49986         var width = typeof this.dot_size === 'function' ? this.dot_size() : this.dot_size;
49987         ctx.beginPath();
49988         this.drawCurveSegment(point.x, point.y, width);
49989         ctx.closePath();
49990         ctx.fillStyle = color;
49991         ctx.fill();
49992     },
49993     
49994     drawCurve: function (_a) {
49995         var color = _a.color, curve = _a.curve;
49996         var ctx = this.canvasElCtx();
49997         var widthDelta = curve.endWidth - curve.startWidth;
49998         var drawSteps = Math.floor(curve.length()) * 2;
49999         ctx.beginPath();
50000         ctx.fillStyle = color;
50001         for (var i = 0; i < drawSteps; i += 1) {
50002         var t = i / drawSteps;
50003         var tt = t * t;
50004         var ttt = tt * t;
50005         var u = 1 - t;
50006         var uu = u * u;
50007         var uuu = uu * u;
50008         var x = uuu * curve.startPoint.x;
50009         x += 3 * uu * t * curve.control1.x;
50010         x += 3 * u * tt * curve.control2.x;
50011         x += ttt * curve.endPoint.x;
50012         var y = uuu * curve.startPoint.y;
50013         y += 3 * uu * t * curve.control1.y;
50014         y += 3 * u * tt * curve.control2.y;
50015         y += ttt * curve.endPoint.y;
50016         var width = curve.startWidth + ttt * widthDelta;
50017         this.drawCurveSegment(x, y, width);
50018         }
50019         ctx.closePath();
50020         ctx.fill();
50021     },
50022     
50023     drawCurveSegment: function (x, y, width) {
50024         var ctx = this.canvasElCtx();
50025         ctx.moveTo(x, y);
50026         ctx.arc(x, y, width, 0, 2 * Math.PI, false);
50027         this.is_empty = false;
50028     },
50029     
50030     clear: function()
50031     {
50032         var ctx = this.canvasElCtx();
50033         var canvas = this.canvasEl().dom;
50034         ctx.fillStyle = this.bg_color;
50035         ctx.clearRect(0, 0, canvas.width, canvas.height);
50036         ctx.fillRect(0, 0, canvas.width, canvas.height);
50037         this.curve_data = [];
50038         this.reset();
50039         this.is_empty = true;
50040     },
50041     
50042     fileEl: function()
50043     {
50044         return  this.el.select('input',true).first();
50045     },
50046     
50047     canvasEl: function()
50048     {
50049         return this.el.select('canvas',true).first();
50050     },
50051     
50052     canvasElCtx: function()
50053     {
50054         return this.el.select('canvas',true).first().dom.getContext('2d');
50055     },
50056     
50057     getImage: function(type)
50058     {
50059         if(this.is_empty) {
50060             return false;
50061         }
50062         
50063         // encryption ?
50064         return this.canvasEl().dom.toDataURL('image/'+type, 1);
50065     },
50066     
50067     drawFromImage: function(img_src)
50068     {
50069         var img = new Image();
50070         
50071         img.onload = function(){
50072             this.canvasElCtx().drawImage(img, 0, 0);
50073         }.bind(this);
50074         
50075         img.src = img_src;
50076         
50077         this.is_empty = false;
50078     },
50079     
50080     selectImage: function()
50081     {
50082         this.fileEl().dom.click();
50083     },
50084     
50085     uploadImage: function(e)
50086     {
50087         var reader = new FileReader();
50088         
50089         reader.onload = function(e){
50090             var img = new Image();
50091             img.onload = function(){
50092                 this.reset();
50093                 this.canvasElCtx().drawImage(img, 0, 0);
50094             }.bind(this);
50095             img.src = e.target.result;
50096         }.bind(this);
50097         
50098         reader.readAsDataURL(e.target.files[0]);
50099     },
50100     
50101     // Bezier Point Constructor
50102     Point: (function () {
50103         function Point(x, y, time) {
50104             this.x = x;
50105             this.y = y;
50106             this.time = time || Date.now();
50107         }
50108         Point.prototype.distanceTo = function (start) {
50109             return Math.sqrt(Math.pow(this.x - start.x, 2) + Math.pow(this.y - start.y, 2));
50110         };
50111         Point.prototype.equals = function (other) {
50112             return this.x === other.x && this.y === other.y && this.time === other.time;
50113         };
50114         Point.prototype.velocityFrom = function (start) {
50115             return this.time !== start.time
50116             ? this.distanceTo(start) / (this.time - start.time)
50117             : 0;
50118         };
50119         return Point;
50120     }()),
50121     
50122     
50123     // Bezier Constructor
50124     Bezier: (function () {
50125         function Bezier(startPoint, control2, control1, endPoint, startWidth, endWidth) {
50126             this.startPoint = startPoint;
50127             this.control2 = control2;
50128             this.control1 = control1;
50129             this.endPoint = endPoint;
50130             this.startWidth = startWidth;
50131             this.endWidth = endWidth;
50132         }
50133         Bezier.fromPoints = function (points, widths, scope) {
50134             var c2 = this.calculateControlPoints(points[0], points[1], points[2], scope).c2;
50135             var c3 = this.calculateControlPoints(points[1], points[2], points[3], scope).c1;
50136             return new Bezier(points[1], c2, c3, points[2], widths.start, widths.end);
50137         };
50138         Bezier.calculateControlPoints = function (s1, s2, s3, scope) {
50139             var dx1 = s1.x - s2.x;
50140             var dy1 = s1.y - s2.y;
50141             var dx2 = s2.x - s3.x;
50142             var dy2 = s2.y - s3.y;
50143             var m1 = { x: (s1.x + s2.x) / 2.0, y: (s1.y + s2.y) / 2.0 };
50144             var m2 = { x: (s2.x + s3.x) / 2.0, y: (s2.y + s3.y) / 2.0 };
50145             var l1 = Math.sqrt(dx1 * dx1 + dy1 * dy1);
50146             var l2 = Math.sqrt(dx2 * dx2 + dy2 * dy2);
50147             var dxm = m1.x - m2.x;
50148             var dym = m1.y - m2.y;
50149             var k = l2 / (l1 + l2);
50150             var cm = { x: m2.x + dxm * k, y: m2.y + dym * k };
50151             var tx = s2.x - cm.x;
50152             var ty = s2.y - cm.y;
50153             return {
50154                 c1: new scope.Point(m1.x + tx, m1.y + ty),
50155                 c2: new scope.Point(m2.x + tx, m2.y + ty)
50156             };
50157         };
50158         Bezier.prototype.length = function () {
50159             var steps = 10;
50160             var length = 0;
50161             var px;
50162             var py;
50163             for (var i = 0; i <= steps; i += 1) {
50164                 var t = i / steps;
50165                 var cx = this.point(t, this.startPoint.x, this.control1.x, this.control2.x, this.endPoint.x);
50166                 var cy = this.point(t, this.startPoint.y, this.control1.y, this.control2.y, this.endPoint.y);
50167                 if (i > 0) {
50168                     var xdiff = cx - px;
50169                     var ydiff = cy - py;
50170                     length += Math.sqrt(xdiff * xdiff + ydiff * ydiff);
50171                 }
50172                 px = cx;
50173                 py = cy;
50174             }
50175             return length;
50176         };
50177         Bezier.prototype.point = function (t, start, c1, c2, end) {
50178             return (start * (1.0 - t) * (1.0 - t) * (1.0 - t))
50179             + (3.0 * c1 * (1.0 - t) * (1.0 - t) * t)
50180             + (3.0 * c2 * (1.0 - t) * t * t)
50181             + (end * t * t * t);
50182         };
50183         return Bezier;
50184     }()),
50185     
50186     throttleStroke: function(fn, wait) {
50187       if (wait === void 0) { wait = 250; }
50188       var previous = 0;
50189       var timeout = null;
50190       var result;
50191       var storedContext;
50192       var storedArgs;
50193       var later = function () {
50194           previous = Date.now();
50195           timeout = null;
50196           result = fn.apply(storedContext, storedArgs);
50197           if (!timeout) {
50198               storedContext = null;
50199               storedArgs = [];
50200           }
50201       };
50202       return function wrapper() {
50203           var args = [];
50204           for (var _i = 0; _i < arguments.length; _i++) {
50205               args[_i] = arguments[_i];
50206           }
50207           var now = Date.now();
50208           var remaining = wait - (now - previous);
50209           storedContext = this;
50210           storedArgs = args;
50211           if (remaining <= 0 || remaining > wait) {
50212               if (timeout) {
50213                   clearTimeout(timeout);
50214                   timeout = null;
50215               }
50216               previous = now;
50217               result = fn.apply(storedContext, storedArgs);
50218               if (!timeout) {
50219                   storedContext = null;
50220                   storedArgs = [];
50221               }
50222           }
50223           else if (!timeout) {
50224               timeout = window.setTimeout(later, remaining);
50225           }
50226           return result;
50227       };
50228   }
50229   
50230 });
50231
50232  
50233
50234  // old names for form elements
50235 Roo.bootstrap.Form          =   Roo.bootstrap.form.Form;
50236 Roo.bootstrap.Input         =   Roo.bootstrap.form.Input;
50237 Roo.bootstrap.TextArea      =   Roo.bootstrap.form.TextArea;
50238 Roo.bootstrap.TriggerField  =   Roo.bootstrap.form.TriggerField;
50239 Roo.bootstrap.ComboBox      =   Roo.bootstrap.form.ComboBox;
50240 Roo.bootstrap.DateField     =   Roo.bootstrap.form.DateField;
50241 Roo.bootstrap.TimeField     =   Roo.bootstrap.form.TimeField;
50242 Roo.bootstrap.MonthField    =   Roo.bootstrap.form.MonthField;
50243 Roo.bootstrap.CheckBox      =   Roo.bootstrap.form.CheckBox;
50244 Roo.bootstrap.Radio         =   Roo.bootstrap.form.Radio;
50245 Roo.bootstrap.RadioSet      =   Roo.bootstrap.form.RadioSet;
50246 Roo.bootstrap.SecurePass    =   Roo.bootstrap.form.SecurePass;
50247 Roo.bootstrap.FieldLabel    =   Roo.bootstrap.form.FieldLabel;
50248 Roo.bootstrap.DateSplitField=   Roo.bootstrap.form.DateSplitField;
50249 Roo.bootstrap.NumberField   =   Roo.bootstrap.form.NumberField;
50250 Roo.bootstrap.PhoneInput    =   Roo.bootstrap.form.PhoneInput;
50251 Roo.bootstrap.PhoneInputData=   Roo.bootstrap.form.PhoneInputData;
50252 Roo.bootstrap.MoneyField    =   Roo.bootstrap.form.MoneyField;
50253 Roo.bootstrap.HtmlEditor    =   Roo.bootstrap.form.HtmlEditor;
50254 Roo.bootstrap.HtmlEditor.ToolbarStandard =   Roo.bootstrap.form.HtmlEditorToolbarStandard;
50255 Roo.bootstrap.Markdown      = Roo.bootstrap.form.Markdown;
50256 Roo.bootstrap.CardUploader  = Roo.bootstrap.form.CardUploader;// depricated.
50257 Roo.bootstrap.Navbar            = Roo.bootstrap.nav.Bar;
50258 Roo.bootstrap.NavGroup          = Roo.bootstrap.nav.Group;
50259 Roo.bootstrap.NavHeaderbar      = Roo.bootstrap.nav.Headerbar;
50260 Roo.bootstrap.NavItem           = Roo.bootstrap.nav.Item;
50261
50262 Roo.bootstrap.NavProgressBar     = Roo.bootstrap.nav.ProgressBar;
50263 Roo.bootstrap.NavProgressBarItem = Roo.bootstrap.nav.ProgressBarItem;
50264
50265 Roo.bootstrap.NavSidebar        = Roo.bootstrap.nav.Sidebar;
50266 Roo.bootstrap.NavSidebarItem    = Roo.bootstrap.nav.SidebarItem;
50267
50268 Roo.bootstrap.NavSimplebar      = Roo.bootstrap.nav.Simplebar;// deprciated 
50269 Roo.bootstrap.Menu = Roo.bootstrap.menu.Menu;
50270 Roo.bootstrap.MenuItem =  Roo.bootstrap.menu.Item;
50271 Roo.bootstrap.MenuSeparator = Roo.bootstrap.menu.Separator
50272