roojs-bootstrap.js
[roojs1] / roojs-bootstrap-debug.js
1 Roo.bootstrap = {};/**
2  * set the version of bootstrap based on the stylesheet...
3  *
4  */
5
6 Roo.bootstrap.version = ( function() {
7     var ret=3;
8     Roo.each(document.styleSheets, function(s) {
9         if ( s.href  && s.href.match(/css-bootstrap4/)) {
10             ret=4;
11         }
12     });
13     if (ret > 3) {
14          Roo.Element.prototype.visibilityMode = Roo.Element.DISPLAY;
15     }
16     return ret;
17 })(); Roo.bootstrap.menu = Roo.bootstrap.menu || {};
18 Roo.bootstrap.nav = {};
19
20 Roo.bootstrap.form = {};Roo.bootstrap.panel = {};Roo.bootstrap.layout = {};
21 Roo.htmleditor = {};
22 Roo.namespace('Roo.bootstrap.form.HtmlEditorToolbar');
23 /*
24  * Based on:
25  * Ext JS Library 1.1.1
26  * Copyright(c) 2006-2007, Ext JS, LLC.
27  *
28  * Originally Released Under LGPL - original licence link has changed is not relivant.
29  *
30  * Fork - LGPL
31  * <script type="text/javascript">
32  */
33
34
35 /**
36  * @class Roo.Shadow
37  * Simple class that can provide a shadow effect for any element.  Note that the element MUST be absolutely positioned,
38  * and the shadow does not provide any shimming.  This should be used only in simple cases -- for more advanced
39  * functionality that can also provide the same shadow effect, see the {@link Roo.Layer} class.
40  * @constructor
41  * Create a new Shadow
42  * @param {Object} config The config object
43  */
44 Roo.Shadow = function(config){
45     Roo.apply(this, config);
46     if(typeof this.mode != "string"){
47         this.mode = this.defaultMode;
48     }
49     var o = this.offset, a = {h: 0};
50     var rad = Math.floor(this.offset/2);
51     switch(this.mode.toLowerCase()){ // all this hideous nonsense calculates the various offsets for shadows
52         case "drop":
53             a.w = 0;
54             a.l = a.t = o;
55             a.t -= 1;
56             if(Roo.isIE){
57                 a.l -= this.offset + rad;
58                 a.t -= this.offset + rad;
59                 a.w -= rad;
60                 a.h -= rad;
61                 a.t += 1;
62             }
63         break;
64         case "sides":
65             a.w = (o*2);
66             a.l = -o;
67             a.t = o-1;
68             if(Roo.isIE){
69                 a.l -= (this.offset - rad);
70                 a.t -= this.offset + rad;
71                 a.l += 1;
72                 a.w -= (this.offset - rad)*2;
73                 a.w -= rad + 1;
74                 a.h -= 1;
75             }
76         break;
77         case "frame":
78             a.w = a.h = (o*2);
79             a.l = a.t = -o;
80             a.t += 1;
81             a.h -= 2;
82             if(Roo.isIE){
83                 a.l -= (this.offset - rad);
84                 a.t -= (this.offset - rad);
85                 a.l += 1;
86                 a.w -= (this.offset + rad + 1);
87                 a.h -= (this.offset + rad);
88                 a.h += 1;
89             }
90         break;
91     };
92
93     this.adjusts = a;
94 };
95
96 Roo.Shadow.prototype = {
97     /**
98      * @cfg {String} mode
99      * The shadow display mode.  Supports the following options:<br />
100      * sides: Shadow displays on both sides and bottom only<br />
101      * frame: Shadow displays equally on all four sides<br />
102      * drop: Traditional bottom-right drop shadow (default)
103      */
104     mode: false,
105     /**
106      * @cfg {String} offset
107      * The number of pixels to offset the shadow from the element (defaults to 4)
108      */
109     offset: 4,
110
111     // private
112     defaultMode: "drop",
113
114     /**
115      * Displays the shadow under the target element
116      * @param {String/HTMLElement/Element} targetEl The id or element under which the shadow should display
117      */
118     show : function(target){
119         target = Roo.get(target);
120         if(!this.el){
121             this.el = Roo.Shadow.Pool.pull();
122             if(this.el.dom.nextSibling != target.dom){
123                 this.el.insertBefore(target);
124             }
125         }
126         this.el.setStyle("z-index", this.zIndex || parseInt(target.getStyle("z-index"), 10)-1);
127         if(Roo.isIE){
128             this.el.dom.style.filter="progid:DXImageTransform.Microsoft.alpha(opacity=50) progid:DXImageTransform.Microsoft.Blur(pixelradius="+(this.offset)+")";
129         }
130         this.realign(
131             target.getLeft(true),
132             target.getTop(true),
133             target.getWidth(),
134             target.getHeight()
135         );
136         this.el.dom.style.display = "block";
137     },
138
139     /**
140      * Returns true if the shadow is visible, else false
141      */
142     isVisible : function(){
143         return this.el ? true : false;  
144     },
145
146     /**
147      * Direct alignment when values are already available. Show must be called at least once before
148      * calling this method to ensure it is initialized.
149      * @param {Number} left The target element left position
150      * @param {Number} top The target element top position
151      * @param {Number} width The target element width
152      * @param {Number} height The target element height
153      */
154     realign : function(l, t, w, h){
155         if(!this.el){
156             return;
157         }
158         var a = this.adjusts, d = this.el.dom, s = d.style;
159         var iea = 0;
160         s.left = (l+a.l)+"px";
161         s.top = (t+a.t)+"px";
162         var sw = (w+a.w), sh = (h+a.h), sws = sw +"px", shs = sh + "px";
163  
164         if(s.width != sws || s.height != shs){
165             s.width = sws;
166             s.height = shs;
167             if(!Roo.isIE){
168                 var cn = d.childNodes;
169                 var sww = Math.max(0, (sw-12))+"px";
170                 cn[0].childNodes[1].style.width = sww;
171                 cn[1].childNodes[1].style.width = sww;
172                 cn[2].childNodes[1].style.width = sww;
173                 cn[1].style.height = Math.max(0, (sh-12))+"px";
174             }
175         }
176     },
177
178     /**
179      * Hides this shadow
180      */
181     hide : function(){
182         if(this.el){
183             this.el.dom.style.display = "none";
184             Roo.Shadow.Pool.push(this.el);
185             delete this.el;
186         }
187     },
188
189     /**
190      * Adjust the z-index of this shadow
191      * @param {Number} zindex The new z-index
192      */
193     setZIndex : function(z){
194         this.zIndex = z;
195         if(this.el){
196             this.el.setStyle("z-index", z);
197         }
198     }
199 };
200
201 // Private utility class that manages the internal Shadow cache
202 Roo.Shadow.Pool = function(){
203     var p = [];
204     var markup = Roo.isIE ?
205                  '<div class="x-ie-shadow"></div>' :
206                  '<div class="x-shadow"><div class="xst"><div class="xstl"></div><div class="xstc"></div><div class="xstr"></div></div><div class="xsc"><div class="xsml"></div><div class="xsmc"></div><div class="xsmr"></div></div><div class="xsb"><div class="xsbl"></div><div class="xsbc"></div><div class="xsbr"></div></div></div>';
207     return {
208         pull : function(){
209             var sh = p.shift();
210             if(!sh){
211                 sh = Roo.get(Roo.DomHelper.insertHtml("beforeBegin", document.body.firstChild, markup));
212                 sh.autoBoxAdjust = false;
213             }
214             return sh;
215         },
216
217         push : function(sh){
218             p.push(sh);
219         }
220     };
221 }();/*
222  * - LGPL
223  *
224  * base class for bootstrap elements.
225  * 
226  */
227
228 Roo.bootstrap = Roo.bootstrap || {};
229 /**
230  * @class Roo.bootstrap.Component
231  * @extends Roo.Component
232  * @abstract
233  * @children Roo.bootstrap.Component
234  * Bootstrap Component base class
235  * @cfg {String} cls css class
236  * @cfg {String} style any extra css
237  * @cfg {Object} xattr extra attributes to add to 'element' (used by builder to store stuff.)
238  * @cfg {Boolean} can_build_overlaid  True if element can be rebuild from a HTML page
239  * @cfg {string} dataId cutomer id
240  * @cfg {string} name Specifies name attribute
241  * @cfg {string} tooltip  Text for the tooltip
242  * @cfg {string} container_method method to fetch parents container element (used by NavHeaderbar -  getHeaderChildContainer)
243  * @cfg {string|object} visibilityEl (el|parent) What element to use for visibility (@see getVisibilityEl())
244  
245  * @constructor
246  * Do not use directly - it does not do anything..
247  * @param {Object} config The config object
248  */
249
250
251
252 Roo.bootstrap.Component = function(config){
253     Roo.bootstrap.Component.superclass.constructor.call(this, config);
254        
255     this.addEvents({
256         /**
257          * @event childrenrendered
258          * Fires when the children have been rendered..
259          * @param {Roo.bootstrap.Component} this
260          */
261         "childrenrendered" : true
262         
263         
264         
265     });
266     
267     
268 };
269
270 Roo.extend(Roo.bootstrap.Component, Roo.BoxComponent,  {
271     
272     
273     allowDomMove : false, // to stop relocations in parent onRender...
274     
275     cls : false,
276     
277     style : false,
278     
279     autoCreate : false,
280     
281     tooltip : null,
282     /**
283      * Initialize Events for the element
284      */
285     initEvents : function() { },
286     
287     xattr : false,
288     
289     parentId : false,
290     
291     can_build_overlaid : true,
292     
293     container_method : false,
294     
295     dataId : false,
296     
297     name : false,
298     
299     parent: function() {
300         // returns the parent component..
301         return Roo.ComponentMgr.get(this.parentId)
302         
303         
304     },
305     
306     // private
307     onRender : function(ct, position)
308     {
309        // Roo.log("Call onRender: " + this.xtype);
310         
311         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
312         
313         if(this.el){
314             if (this.el.attr('xtype')) {
315                 this.el.attr('xtypex', this.el.attr('xtype'));
316                 this.el.dom.removeAttribute('xtype');
317                 
318                 this.initEvents();
319             }
320             
321             return;
322         }
323         
324          
325         
326         var cfg = Roo.apply({},  this.getAutoCreate());
327         
328         cfg.id = this.id || Roo.id();
329         
330         // fill in the extra attributes 
331         if (this.xattr && typeof(this.xattr) =='object') {
332             for (var i in this.xattr) {
333                 cfg[i] = this.xattr[i];
334             }
335         }
336         
337         if(this.dataId){
338             cfg.dataId = this.dataId;
339         }
340         
341         if (this.cls) {
342             cfg.cls = (typeof(cfg.cls) == 'undefined' ? this.cls : cfg.cls) + ' ' + this.cls;
343         }
344         
345         if (this.style) { // fixme needs to support more complex style data.
346             cfg.style = (typeof(cfg.style) == 'undefined' ? this.style : cfg.style) + '; ' + this.style;
347         }
348         
349         if(this.name){
350             cfg.name = this.name;
351         }
352         
353         this.el = ct.createChild(cfg, position);
354         
355         if (this.tooltip) {
356             this.tooltipEl().attr('tooltip', this.tooltip);
357         }
358         
359         if(this.tabIndex !== undefined){
360             this.el.dom.setAttribute('tabIndex', this.tabIndex);
361         }
362         
363         this.initEvents();
364         
365     },
366     /**
367      * Fetch the element to add children to
368      * @return {Roo.Element} defaults to this.el
369      */
370     getChildContainer : function()
371     {
372         return this.el;
373     },
374     getDocumentBody : function() // used by menus - as they are attached to the body so zIndexes work
375     {
376         return Roo.get(document.body);
377     },
378     
379     /**
380      * Fetch the element to display the tooltip on.
381      * @return {Roo.Element} defaults to this.el
382      */
383     tooltipEl : function()
384     {
385         return this.el;
386     },
387         
388     addxtype  : function(tree,cntr)
389     {
390         var cn = this;
391         
392         cn = Roo.factory(tree);
393         //Roo.log(['addxtype', cn]);
394            
395         cn.parentType = this.xtype; //??
396         cn.parentId = this.id;
397         
398         cntr = (typeof(cntr) == 'undefined' ) ? 'getChildContainer' : cntr;
399         if (typeof(cn.container_method) == 'string') {
400             cntr = cn.container_method;
401         }
402         
403         
404         var has_flexy_each =  (typeof(tree['flexy:foreach']) != 'undefined');
405         
406         var has_flexy_if =  (typeof(tree['flexy:if']) != 'undefined');
407         
408         var build_from_html =  Roo.XComponent.build_from_html;
409           
410         var is_body  = (tree.xtype == 'Body') ;
411           
412         var page_has_body = (Roo.get(document.body).attr('xtype') == 'Roo.bootstrap.Body');
413           
414         var self_cntr_el = Roo.get(this[cntr](false));
415         
416         // do not try and build conditional elements 
417         if ((has_flexy_each || has_flexy_if || this.can_build_overlaid == false ) && build_from_html) {
418             return false;
419         }
420         
421         if (!has_flexy_each || !build_from_html || is_body || !page_has_body) {
422             if(!has_flexy_if || typeof(tree.name) == 'undefined' || !build_from_html || is_body || !page_has_body){
423                 return this.addxtypeChild(tree,cntr, is_body);
424             }
425             
426             var echild =self_cntr_el ? self_cntr_el.child('>*[name=' + tree.name + ']') : false;
427                 
428             if(echild){
429                 return this.addxtypeChild(Roo.apply({}, tree),cntr);
430             }
431             
432             Roo.log('skipping render');
433             return cn;
434             
435         }
436         
437         var ret = false;
438         if (!build_from_html) {
439             return false;
440         }
441         
442         // this i think handles overlaying multiple children of the same type
443         // with the sam eelement.. - which might be buggy..
444         while (true) {
445             var echild =self_cntr_el ? self_cntr_el.child('>*[xtype]') : false;
446             
447             if (!echild) {
448                 break;
449             }
450             
451             if (echild && echild.attr('xtype').split('.').pop() != cn.xtype) {
452                 break;
453             }
454             
455             ret = this.addxtypeChild(Roo.apply({}, tree),cntr);
456         }
457        
458         return ret;
459     },
460     
461     
462     addxtypeChild : function (tree, cntr, is_body)
463     {
464         Roo.debug && Roo.log('addxtypeChild:' + cntr);
465         Roo.log('ADDXTYPECHILD');
466         var cn = this;
467         cntr = (typeof(cntr) == 'undefined' ) ? 'getChildContainer' : cntr;
468         
469         
470         var has_flexy = (typeof(tree['flexy:if']) != 'undefined') ||
471                     (typeof(tree['flexy:foreach']) != 'undefined');
472           
473     
474         
475         skip_children = false;
476         // render the element if it's not BODY.
477         if (!is_body) {
478             
479             // if parent was disabled, then do not try and create the children..
480             if(!this[cntr](true)){
481                 tree.items = [];
482                 return tree;
483             }
484            
485             cn = Roo.factory(tree);
486            
487             cn.parentType = this.xtype; //??
488             cn.parentId = this.id;
489             
490             var build_from_html =  Roo.XComponent.build_from_html;
491             
492             
493             // does the container contain child eleemnts with 'xtype' attributes.
494             // that match this xtype..
495             // note - when we render we create these as well..
496             // so we should check to see if body has xtype set.
497             if (build_from_html && Roo.get(document.body).attr('xtype') == 'Roo.bootstrap.Body') {
498                
499                 var self_cntr_el = Roo.get(this[cntr](false));
500                 var echild =self_cntr_el ? self_cntr_el.child('>*[xtype]') : false;
501                 if (echild) { 
502                     //Roo.log(Roo.XComponent.build_from_html);
503                     //Roo.log("got echild:");
504                     //Roo.log(echild);
505                 }
506                 // there is a scenario where some of the child elements are flexy:if (and all of the same type)
507                 // and are not displayed -this causes this to use up the wrong element when matching.
508                 // at present the only work around for this is to nest flexy:if elements in another element that is always rendered.
509                 
510                 
511                 if (echild && echild.attr('xtype').split('.').pop() == cn.xtype) {
512                   //  Roo.log("found child for " + this.xtype +": " + echild.attr('xtype') );
513                   
514                   
515                   
516                     cn.el = echild;
517                   //  Roo.log("GOT");
518                     //echild.dom.removeAttribute('xtype');
519                 } else {
520                     Roo.debug && Roo.log("MISSING " + cn.xtype + " on child of " + (this.el ? this.el.attr('xbuilderid') : 'no parent'));
521                     Roo.debug && Roo.log(self_cntr_el);
522                     Roo.debug && Roo.log(echild);
523                     Roo.debug && Roo.log(cn);
524                 }
525             }
526            
527             
528            
529             // if object has flexy:if - then it may or may not be rendered.
530             if (build_from_html && has_flexy && !cn.el &&  cn.can_build_overlaid) {
531                 // skip a flexy if element.
532                 Roo.debug && Roo.log('skipping render');
533                 Roo.debug && Roo.log(tree);
534                 if (!cn.el) {
535                     Roo.debug && Roo.log('skipping all children');
536                     skip_children = true;
537                 }
538                 
539              } else {
540                  
541                 // actually if flexy:foreach is found, we really want to create 
542                 // multiple copies here...
543                 //Roo.log('render');
544                 //Roo.log(this[cntr]());
545                 // some elements do not have render methods.. like the layouts...
546                 /*
547                 if(this[cntr](true) === false){
548                     cn.items = [];
549                     return cn;
550                 }
551                 */
552                 cn.render && cn.render(this[cntr](true));
553                 
554              }
555             // then add the element..
556         }
557          
558         // handle the kids..
559         
560         var nitems = [];
561         /*
562         if (typeof (tree.menu) != 'undefined') {
563             tree.menu.parentType = cn.xtype;
564             tree.menu.triggerEl = cn.el;
565             nitems.push(cn.addxtype(Roo.apply({}, tree.menu)));
566             
567         }
568         */
569         if (!tree.items || !tree.items.length) {
570             cn.items = nitems;
571             //Roo.log(["no children", this]);
572             
573             return cn;
574         }
575          
576         var items = tree.items;
577         delete tree.items;
578         
579         //Roo.log(items.length);
580             // add the items..
581         if (!skip_children) {    
582             for(var i =0;i < items.length;i++) {
583               //  Roo.log(['add child', items[i]]);
584                 nitems.push(cn.addxtype(Roo.apply({}, items[i])));
585             }
586         }
587         
588         cn.items = nitems;
589         
590         //Roo.log("fire childrenrendered");
591         
592         cn.fireEvent('childrenrendered', this);
593         
594         return cn;
595     },
596     
597     /**
598      * Set the element that will be used to show or hide
599      */
600     setVisibilityEl : function(el)
601     {
602         this.visibilityEl = el;
603     },
604     
605      /**
606      * Get the element that will be used to show or hide
607      */
608     getVisibilityEl : function()
609     {
610         if (typeof(this.visibilityEl) == 'object') {
611             return this.visibilityEl;
612         }
613         
614         if (typeof(this.visibilityEl) == 'string') {
615             return this.visibilityEl == 'parent' ? this.parent().getEl() : this.getEl();
616         }
617         
618         return this.getEl();
619     },
620     
621     /**
622      * Show a component - removes 'hidden' class
623      */
624     show : function()
625     {
626         if(!this.getVisibilityEl()){
627             return;
628         }
629          
630         this.getVisibilityEl().removeClass(['hidden','d-none']);
631         
632         this.fireEvent('show', this);
633         
634         
635     },
636     /**
637      * Hide a component - adds 'hidden' class
638      */
639     hide: function()
640     {
641         if(!this.getVisibilityEl()){
642             return;
643         }
644         
645         this.getVisibilityEl().addClass(['hidden','d-none']);
646         
647         this.fireEvent('hide', this);
648         
649     }
650 });
651
652  /*
653  * - LGPL
654  *
655  * element
656  * 
657  */
658
659 /**
660  * @class Roo.bootstrap.Element
661  * @extends Roo.bootstrap.Component
662  * @children Roo.bootstrap.Component
663  * Bootstrap Element class (basically a DIV used to make random stuff )
664  * 
665  * @cfg {String} html contents of the element
666  * @cfg {String} tag tag of the element
667  * @cfg {String} cls class of the element
668  * @cfg {Boolean} preventDefault (true|false) default false
669  * @cfg {Boolean} clickable (true|false) default false
670  * @cfg {String} role default blank - set to button to force cursor pointer
671  
672  * 
673  * @constructor
674  * Create a new Element
675  * @param {Object} config The config object
676  */
677
678 Roo.bootstrap.Element = function(config){
679     Roo.bootstrap.Element.superclass.constructor.call(this, config);
680     
681     this.addEvents({
682         // raw events
683         /**
684          * @event click
685          * When a element is chick
686          * @param {Roo.bootstrap.Element} this
687          * @param {Roo.EventObject} e
688          */
689         "click" : true 
690         
691       
692     });
693 };
694
695 Roo.extend(Roo.bootstrap.Element, Roo.bootstrap.Component,  {
696     
697     tag: 'div',
698     cls: '',
699     html: '',
700     preventDefault: false, 
701     clickable: false,
702     tapedTwice : false,
703     role : false,
704     
705     getAutoCreate : function(){
706         
707         var cfg = {
708             tag: this.tag,
709             // cls: this.cls, double assign in parent class Component.js :: onRender
710             html: this.html
711         };
712         if (this.role !== false) {
713             cfg.role = this.role;
714         }
715         
716         return cfg;
717     },
718     
719     initEvents: function() 
720     {
721         Roo.bootstrap.Element.superclass.initEvents.call(this);
722         
723         if(this.clickable){
724             this.el.on('click', this.onClick, this);
725         }
726         
727         
728     },
729     
730     onClick : function(e)
731     {
732         if(this.preventDefault){
733             e.preventDefault();
734         }
735         
736         this.fireEvent('click', this, e); // why was this double click before?
737     },
738     
739     
740     
741
742     
743     
744     getValue : function()
745     {
746         return this.el.dom.innerHTML;
747     },
748     
749     setValue : function(value)
750     {
751         this.el.dom.innerHTML = value;
752     }
753    
754 });
755
756  
757
758  /*
759  * - LGPL
760  *
761  * dropable area
762  * 
763  */
764
765 /**
766  * @class Roo.bootstrap.DropTarget
767  * @extends Roo.bootstrap.Element
768  * Bootstrap DropTarget class
769  
770  * @cfg {string} name dropable name
771  * 
772  * @constructor
773  * Create a new Dropable Area
774  * @param {Object} config The config object
775  */
776
777 Roo.bootstrap.DropTarget = function(config){
778     Roo.bootstrap.DropTarget.superclass.constructor.call(this, config);
779     
780     this.addEvents({
781         // raw events
782         /**
783          * @event click
784          * When a element is chick
785          * @param {Roo.bootstrap.Element} this
786          * @param {Roo.EventObject} e
787          */
788         "drop" : true
789     });
790 };
791
792 Roo.extend(Roo.bootstrap.DropTarget, Roo.bootstrap.Element,  {
793     
794     
795     getAutoCreate : function(){
796         
797          
798     },
799     
800     initEvents: function() 
801     {
802         Roo.bootstrap.DropTarget.superclass.initEvents.call(this);
803         this.dropZone = new Roo.dd.DropTarget(this.getEl(), {
804             ddGroup: this.name,
805             listeners : {
806                 drop : this.dragDrop.createDelegate(this),
807                 enter : this.dragEnter.createDelegate(this),
808                 out : this.dragOut.createDelegate(this),
809                 over : this.dragOver.createDelegate(this)
810             }
811             
812         });
813         this.dropZone.DDM.useCache = false // so data gets refreshed when we resize stuff
814     },
815     
816     dragDrop : function(source,e,data)
817     {
818         // user has to decide how to impliment this.
819         Roo.log('drop');
820         Roo.log(this);
821         //this.fireEvent('drop', this, source, e ,data);
822         return false;
823     },
824     
825     dragEnter : function(n, dd, e, data)
826     {
827         // probably want to resize the element to match the dropped element..
828         Roo.log("enter");
829         this.originalSize = this.el.getSize();
830         this.el.setSize( n.el.getSize());
831         this.dropZone.DDM.refreshCache(this.name);
832         Roo.log([n, dd, e, data]);
833     },
834     
835     dragOut : function(value)
836     {
837         // resize back to normal
838         Roo.log("out");
839         this.el.setSize(this.originalSize);
840         this.dropZone.resetConstraints();
841     },
842     
843     dragOver : function()
844     {
845         // ??? do nothing?
846     }
847    
848 });
849
850  
851
852  /*
853  * - LGPL
854  *
855  * Body
856  *
857  */
858
859 /**
860  * @class Roo.bootstrap.Body
861  * @extends Roo.bootstrap.Component
862  * @children Roo.bootstrap.Component 
863  * @parent none builder
864  * Bootstrap Body class
865  *
866  * @constructor
867  * Create a new body
868  * @param {Object} config The config object
869  */
870
871 Roo.bootstrap.Body = function(config){
872
873     config = config || {};
874
875     Roo.bootstrap.Body.superclass.constructor.call(this, config);
876     this.el = Roo.get(config.el ? config.el : document.body );
877     if (this.cls && this.cls.length) {
878         Roo.get(document.body).addClass(this.cls);
879     }
880 };
881
882 Roo.extend(Roo.bootstrap.Body, Roo.bootstrap.Component,  {
883
884     is_body : true,// just to make sure it's constructed?
885
886         autoCreate : {
887         cls: 'container'
888     },
889     onRender : function(ct, position)
890     {
891        /* Roo.log("Roo.bootstrap.Body - onRender");
892         if (this.cls && this.cls.length) {
893             Roo.get(document.body).addClass(this.cls);
894         }
895         // style??? xttr???
896         */
897     }
898
899
900
901
902 });
903 /*
904  * - LGPL
905  *
906  * button group
907  * 
908  */
909
910
911 /**
912  * @class Roo.bootstrap.ButtonGroup
913  * @extends Roo.bootstrap.Component
914  * Bootstrap ButtonGroup class
915  * @children Roo.bootstrap.Button Roo.bootstrap.form.Form
916  * 
917  * @cfg {String} size lg | sm | xs (default empty normal)
918  * @cfg {String} align vertical | justified  (default none)
919  * @cfg {String} direction up | down (default down)
920  * @cfg {Boolean} toolbar false | true
921  * @cfg {Boolean} btn true | false
922  * 
923  * 
924  * @constructor
925  * Create a new Input
926  * @param {Object} config The config object
927  */
928
929 Roo.bootstrap.ButtonGroup = function(config){
930     Roo.bootstrap.ButtonGroup.superclass.constructor.call(this, config);
931 };
932
933 Roo.extend(Roo.bootstrap.ButtonGroup, Roo.bootstrap.Component,  {
934     
935     size: '',
936     align: '',
937     direction: '',
938     toolbar: false,
939     btn: true,
940
941     getAutoCreate : function(){
942         var cfg = {
943             cls: 'btn-group',
944             html : null
945         };
946         
947         cfg.html = this.html || cfg.html;
948         
949         if (this.toolbar) {
950             cfg = {
951                 cls: 'btn-toolbar',
952                 html: null
953             };
954             
955             return cfg;
956         }
957         
958         if (['vertical','justified'].indexOf(this.align)!==-1) {
959             cfg.cls = 'btn-group-' + this.align;
960             
961             if (this.align == 'justified') {
962                 console.log(this.items);
963             }
964         }
965         
966         if (['lg','sm','xs'].indexOf(this.size)!==-1) {
967             cfg.cls += ' btn-group-' + this.size;
968         }
969         
970         if (this.direction == 'up') {
971             cfg.cls += ' dropup' ;
972         }
973         
974         return cfg;
975     },
976     /**
977      * Add a button to the group (similar to NavItem API.)
978      */
979     addItem : function(cfg)
980     {
981         var cn = new Roo.bootstrap.Button(cfg);
982         //this.register(cn);
983         cn.parentId = this.id;
984         cn.onRender(this.el, null);
985         return cn;
986     }
987    
988 });
989
990  /*
991  * - LGPL
992  *
993  * button
994  * 
995  */
996
997 /**
998  * @class Roo.bootstrap.Button
999  * @extends Roo.bootstrap.Component
1000  * Bootstrap Button class
1001  * @cfg {String} html The button content
1002  * @cfg {String} weight (default|primary|secondary|success|info|warning|danger|link|light|dark) default
1003  * @cfg {String} badge_weight (default|primary|secondary|success|info|warning|danger|link|light|dark) default (same as button)
1004  * @cfg {Boolean} outline default false (except for weight=default which emulates old behaveiour with an outline)
1005  * @cfg {String} size (lg|sm|xs)
1006  * @cfg {String} tag (a|input|submit)
1007  * @cfg {String} href empty or href
1008  * @cfg {Boolean} disabled default false;
1009  * @cfg {Boolean} isClose default false;
1010  * @cfg {String} glyphicon depricated - use fa
1011  * @cfg {String} fa fontawesome icon - eg. 'comment' - without the fa/fas etc..
1012  * @cfg {String} badge text for badge
1013  * @cfg {String} theme (default|glow)  
1014  * @cfg {Boolean} inverse dark themed version
1015  * @cfg {Boolean} toggle is it a slidy toggle button
1016  * @cfg {Boolean} pressed   default null - if the button ahs active state
1017  * @cfg {String} ontext text for on slidy toggle state
1018  * @cfg {String} offtext text for off slidy toggle state
1019  * @cfg {Boolean} preventDefault  default true (stop click event triggering the URL if it's a link.)
1020  * @cfg {Boolean} removeClass remove the standard class..
1021  * @cfg {String} target (_self|_blank|_parent|_top|other) target for a href. 
1022  * @cfg {Boolean} grpup if parent is a btn group - then it turns it into a toogleGroup.
1023  * @cfg {Roo.bootstrap.menu.Menu} menu a Menu 
1024
1025  * @constructor
1026  * Create a new button
1027  * @param {Object} config The config object
1028  */
1029
1030
1031 Roo.bootstrap.Button = function(config){
1032     Roo.bootstrap.Button.superclass.constructor.call(this, config);
1033     
1034     this.addEvents({
1035         // raw events
1036         /**
1037          * @event click
1038          * When a button is pressed
1039          * @param {Roo.bootstrap.Button} btn
1040          * @param {Roo.EventObject} e
1041          */
1042         "click" : true,
1043         /**
1044          * @event dblclick
1045          * When a button is double clicked
1046          * @param {Roo.bootstrap.Button} btn
1047          * @param {Roo.EventObject} e
1048          */
1049         "dblclick" : true,
1050          /**
1051          * @event toggle
1052          * After the button has been toggles
1053          * @param {Roo.bootstrap.Button} btn
1054          * @param {Roo.EventObject} e
1055          * @param {boolean} pressed (also available as button.pressed)
1056          */
1057         "toggle" : true
1058     });
1059 };
1060
1061 Roo.extend(Roo.bootstrap.Button, Roo.bootstrap.Component,  {
1062     html: false,
1063     active: false,
1064     weight: '',
1065     badge_weight: '',
1066     outline : false,
1067     size: '',
1068     tag: 'button',
1069     href: '',
1070     disabled: false,
1071     isClose: false,
1072     glyphicon: '',
1073     fa: '',
1074     badge: '',
1075     theme: 'default',
1076     inverse: false,
1077     
1078     toggle: false,
1079     ontext: 'ON',
1080     offtext: 'OFF',
1081     defaulton: true,
1082     preventDefault: true,
1083     removeClass: false,
1084     name: false,
1085     target: false,
1086     group : false,
1087      
1088     pressed : null,
1089      
1090     
1091     getAutoCreate : function(){
1092         
1093         var cfg = {
1094             tag : 'button',
1095             cls : 'roo-button',
1096             html: ''
1097         };
1098         
1099         if (['a', 'button', 'input', 'submit'].indexOf(this.tag) < 0) {
1100             throw "Invalid value for tag: " + this.tag + ". must be a, button, input or submit.";
1101             this.tag = 'button';
1102         } else {
1103             cfg.tag = this.tag;
1104         }
1105         cfg.html = '<span class="roo-button-text">' + (this.html || cfg.html) + '</span>';
1106         
1107         if (this.toggle == true) {
1108             cfg={
1109                 tag: 'div',
1110                 cls: 'slider-frame roo-button',
1111                 cn: [
1112                     {
1113                         tag: 'span',
1114                         'data-on-text':'ON',
1115                         'data-off-text':'OFF',
1116                         cls: 'slider-button',
1117                         html: this.offtext
1118                     }
1119                 ]
1120             };
1121             // why are we validating the weights?
1122             if (Roo.bootstrap.Button.weights.indexOf(this.weight) > -1) {
1123                 cfg.cls +=  ' ' + this.weight;
1124             }
1125             
1126             return cfg;
1127         }
1128         
1129         if (this.isClose) {
1130             cfg.cls += ' close';
1131             
1132             cfg["aria-hidden"] = true;
1133             
1134             cfg.html = "&times;";
1135             
1136             return cfg;
1137         }
1138              
1139         
1140         if (this.theme==='default') {
1141             cfg.cls = 'btn roo-button';
1142             
1143             //if (this.parentType != 'Navbar') {
1144             this.weight = this.weight.length ?  this.weight : 'default';
1145             //}
1146             if (Roo.bootstrap.Button.weights.indexOf(this.weight) > -1) {
1147                 
1148                 var outline = this.outline || this.weight == 'default' ? 'outline-' : '';
1149                 var weight = this.weight == 'default' ? 'secondary' : this.weight;
1150                 cfg.cls += ' btn-' + outline + weight;
1151                 if (this.weight == 'default') {
1152                     // BC
1153                     cfg.cls += ' btn-' + this.weight;
1154                 }
1155             }
1156         } else if (this.theme==='glow') {
1157             
1158             cfg.tag = 'a';
1159             cfg.cls = 'btn-glow roo-button';
1160             
1161             if (Roo.bootstrap.Button.weights.indexOf(this.weight) > -1) {
1162                 
1163                 cfg.cls += ' ' + this.weight;
1164             }
1165         }
1166    
1167         
1168         if (this.inverse) {
1169             this.cls += ' inverse';
1170         }
1171         
1172         
1173         if (this.active || this.pressed === true) {
1174             cfg.cls += ' active';
1175         }
1176         
1177         if (this.disabled) {
1178             cfg.disabled = 'disabled';
1179         }
1180         
1181         if (this.items) {
1182             Roo.log('changing to ul' );
1183             cfg.tag = 'ul';
1184             this.glyphicon = 'caret';
1185             if (Roo.bootstrap.version == 4) {
1186                 this.fa = 'caret-down';
1187             }
1188             
1189         }
1190         
1191         cfg.cls += this.size.length ? (' btn-' + this.size) : '';
1192          
1193         //gsRoo.log(this.parentType);
1194         if (this.parentType === 'Navbar' && !this.parent().bar) {
1195             Roo.log('changing to li?');
1196             
1197             cfg.tag = 'li';
1198             
1199             cfg.cls = '';
1200             cfg.cn =  [{
1201                 tag : 'a',
1202                 cls : 'roo-button',
1203                 html : this.html,
1204                 href : this.href || '#'
1205             }];
1206             if (this.menu) {
1207                 cfg.cn[0].html = this.html  + ' <span class="caret"></span>';
1208                 cfg.cls += ' dropdown';
1209             }   
1210             
1211             delete cfg.html;
1212             
1213         }
1214         
1215        cfg.cls += this.parentType === 'Navbar' ?  ' navbar-btn' : '';
1216         
1217         if (this.glyphicon) {
1218             cfg.html = ' ' + cfg.html;
1219             
1220             cfg.cn = [
1221                 {
1222                     tag: 'span',
1223                     cls: 'glyphicon glyphicon-' + this.glyphicon
1224                 }
1225             ];
1226         }
1227         if (this.fa) {
1228             cfg.html = ' ' + cfg.html;
1229             
1230             cfg.cn = [
1231                 {
1232                     tag: 'i',
1233                     cls: 'fa fas fa-' + this.fa
1234                 }
1235             ];
1236         }
1237         
1238         if (this.badge) {
1239             cfg.html += ' ';
1240             
1241             cfg.tag = 'a';
1242             
1243 //            cfg.cls='btn roo-button';
1244             
1245             cfg.href=this.href;
1246             
1247             var value = cfg.html;
1248             
1249             if(this.glyphicon){
1250                 value = {
1251                     tag: 'span',
1252                     cls: 'glyphicon glyphicon-' + this.glyphicon,
1253                     html: this.html
1254                 };
1255             }
1256             if(this.fa){
1257                 value = {
1258                     tag: 'i',
1259                     cls: 'fa fas fa-' + this.fa,
1260                     html: this.html
1261                 };
1262             }
1263             
1264             var bw = this.badge_weight.length ? this.badge_weight :
1265                 (this.weight.length ? this.weight : 'secondary');
1266             bw = bw == 'default' ? 'secondary' : bw;
1267             
1268             cfg.cn = [
1269                 value,
1270                 {
1271                     tag: 'span',
1272                     cls: 'badge badge-' + bw,
1273                     html: this.badge
1274                 }
1275             ];
1276             
1277             cfg.html='';
1278         }
1279         
1280         if (this.menu) {
1281             cfg.cls += ' dropdown';
1282             cfg.html = typeof(cfg.html) != 'undefined' ?
1283                     cfg.html + ' <span class="caret"></span>' : '<span class="caret"></span>';
1284         }
1285         
1286         if (cfg.tag !== 'a' && this.href !== '') {
1287             throw "Tag must be a to set href.";
1288         } else if (this.href.length > 0) {
1289             cfg.href = this.href;
1290         }
1291         
1292         if(this.removeClass){
1293             cfg.cls = '';
1294         }
1295         
1296         if(this.target){
1297             cfg.target = this.target;
1298         }
1299         
1300         return cfg;
1301     },
1302     initEvents: function() {
1303        // Roo.log('init events?');
1304 //        Roo.log(this.el.dom);
1305         // add the menu...
1306         
1307         if (typeof (this.menu) != 'undefined') {
1308             this.menu.parentType = this.xtype;
1309             this.menu.triggerEl = this.el;
1310             this.addxtype(Roo.apply({}, this.menu));
1311         }
1312
1313
1314         if (this.el.hasClass('roo-button')) {
1315              this.el.on('click', this.onClick, this);
1316              this.el.on('dblclick', this.onDblClick, this);
1317         } else {
1318              this.el.select('.roo-button').on('click', this.onClick, this);
1319              this.el.select('.roo-button').on('dblclick', this.onDblClick, this);
1320              
1321         }
1322         // why?
1323         if(this.removeClass){
1324             this.el.on('click', this.onClick, this);
1325         }
1326         
1327         if (this.group === true) {
1328              if (this.pressed === false || this.pressed === true) {
1329                 // nothing
1330             } else {
1331                 this.pressed = false;
1332                 this.setActive(this.pressed);
1333             }
1334             
1335         }
1336         
1337         this.el.enableDisplayMode();
1338         
1339     },
1340     onClick : function(e)
1341     {
1342         if (this.disabled) {
1343             return;
1344         }
1345         
1346         Roo.log('button on click ');
1347         if(this.href === '' || this.preventDefault){
1348             e.preventDefault();
1349         }
1350         
1351         if (this.group) {
1352             if (this.pressed) {
1353                 // do nothing -
1354                 return;
1355             }
1356             this.setActive(true);
1357             var pi = this.parent().items;
1358             for (var i = 0;i < pi.length;i++) {
1359                 if (this == pi[i]) {
1360                     continue;
1361                 }
1362                 if (pi[i].el.hasClass('roo-button')) {
1363                     pi[i].setActive(false);
1364                 }
1365             }
1366             this.fireEvent('click', this, e);            
1367             return;
1368         }
1369         
1370         if (this.pressed === true || this.pressed === false) {
1371             this.toggleActive(e);
1372         }
1373         
1374         
1375         this.fireEvent('click', this, e);
1376     },
1377     onDblClick: function(e)
1378     {
1379         if (this.disabled) {
1380             return;
1381         }
1382         if(this.preventDefault){
1383             e.preventDefault();
1384         }
1385         this.fireEvent('dblclick', this, e);
1386     },
1387     /**
1388      * Enables this button
1389      */
1390     enable : function()
1391     {
1392         this.disabled = false;
1393         this.el.removeClass('disabled');
1394         this.el.dom.removeAttribute("disabled");
1395     },
1396     
1397     /**
1398      * Disable this button
1399      */
1400     disable : function()
1401     {
1402         this.disabled = true;
1403         this.el.addClass('disabled');
1404         this.el.attr("disabled", "disabled")
1405     },
1406      /**
1407      * sets the active state on/off, 
1408      * @param {Boolean} state (optional) Force a particular state
1409      */
1410     setActive : function(v) {
1411         
1412         this.el[v ? 'addClass' : 'removeClass']('active');
1413         this.pressed = v;
1414     },
1415      /**
1416      * toggles the current active state 
1417      */
1418     toggleActive : function(e)
1419     {
1420         this.setActive(!this.pressed); // this modifies pressed...
1421         this.fireEvent('toggle', this, e, this.pressed);
1422     },
1423      /**
1424      * get the current active state
1425      * @return {boolean} true if it's active
1426      */
1427     isActive : function()
1428     {
1429         return this.el.hasClass('active');
1430     },
1431     /**
1432      * set the text of the first selected button
1433      */
1434     setText : function(str)
1435     {
1436         this.el.select('.roo-button-text',true).first().dom.innerHTML = str;
1437     },
1438     /**
1439      * get the text of the first selected button
1440      */
1441     getText : function()
1442     {
1443         return this.el.select('.roo-button-text',true).first().dom.innerHTML;
1444     },
1445     
1446     setWeight : function(str)
1447     {
1448         this.el.removeClass(Roo.bootstrap.Button.weights.map(function(w) { return 'btn-' + w; } ) );
1449         this.el.removeClass(Roo.bootstrap.Button.weights.map(function(w) { return 'btn-outline-' + w; } ) );
1450         this.weight = str;
1451         var outline = this.outline ? 'outline-' : '';
1452         if (str == 'default') {
1453             this.el.addClass('btn-default btn-outline-secondary');        
1454             return;
1455         }
1456         this.el.addClass('btn-' + outline + str);        
1457     }
1458     
1459     
1460 });
1461 // fixme - this is probably generic bootstrap - should go in some kind of enum file.. - like sizes.
1462
1463 Roo.bootstrap.Button.weights = [
1464     'default',
1465     'secondary' ,
1466     'primary',
1467     'success',
1468     'info',
1469     'warning',
1470     'danger',
1471     'link',
1472     'light',
1473     'dark'              
1474    
1475 ];/*
1476  * - LGPL
1477  *
1478  * column
1479  * 
1480  */
1481
1482 /**
1483  * @class Roo.bootstrap.Column
1484  * @extends Roo.bootstrap.Component
1485  * @children Roo.bootstrap.Component
1486  * Bootstrap Column class
1487  * @cfg {Number} xs colspan out of 12 for mobile-sized screens or 0 for hidden
1488  * @cfg {Number} sm colspan out of 12 for tablet-sized screens or 0 for hidden
1489  * @cfg {Number} md colspan out of 12 for computer-sized screens or 0 for hidden
1490  * @cfg {Number} lg colspan out of 12 for large computer-sized screens or 0 for hidden
1491  * @cfg {Number} xsoff colspan offset out of 12 for mobile-sized screens or 0 for hidden
1492  * @cfg {Number} smoff colspan offset out of 12 for tablet-sized screens or 0 for hidden
1493  * @cfg {Number} mdoff colspan offset out of 12 for computer-sized screens or 0 for hidden
1494  * @cfg {Number} lgoff colspan offset out of 12 for large computer-sized screens or 0 for hidden
1495  *
1496  * 
1497  * @cfg {Boolean} hidden (true|false) hide the element
1498  * @cfg {String} alert (success|info|warning|danger) type alert (changes background / border...)
1499  * @cfg {String} fa (ban|check|...) font awesome icon
1500  * @cfg {Number} fasize (1|2|....) font awsome size
1501
1502  * @cfg {String} icon (info-sign|check|...) glyphicon name
1503
1504  * @cfg {String} html content of column.
1505  * 
1506  * @constructor
1507  * Create a new Column
1508  * @param {Object} config The config object
1509  */
1510
1511 Roo.bootstrap.Column = function(config){
1512     Roo.bootstrap.Column.superclass.constructor.call(this, config);
1513 };
1514
1515 Roo.extend(Roo.bootstrap.Column, Roo.bootstrap.Component,  {
1516     
1517     xs: false,
1518     sm: false,
1519     md: false,
1520     lg: false,
1521     xsoff: false,
1522     smoff: false,
1523     mdoff: false,
1524     lgoff: false,
1525     html: '',
1526     offset: 0,
1527     alert: false,
1528     fa: false,
1529     icon : false,
1530     hidden : false,
1531     fasize : 1,
1532     
1533     getAutoCreate : function(){
1534         var cfg = Roo.apply({}, Roo.bootstrap.Column.superclass.getAutoCreate.call(this));
1535         
1536         cfg = {
1537             tag: 'div',
1538             cls: 'column'
1539         };
1540         
1541         var settings=this;
1542         var sizes =   ['xs','sm','md','lg'];
1543         sizes.map(function(size ,ix){
1544             //Roo.log( size + ':' + settings[size]);
1545             
1546             if (settings[size+'off'] !== false) {
1547                 cfg.cls += ' col-' + size + '-offset-' + settings[size+'off'] ;
1548             }
1549             
1550             if (settings[size] === false) {
1551                 return;
1552             }
1553             
1554             if (!settings[size]) { // 0 = hidden
1555                 cfg.cls += ' hidden-' + size + ' hidden-' + size + '-down';
1556                 // bootsrap4
1557                 for (var i = ix; i > -1; i--) {
1558                     cfg.cls +=  ' d-' + sizes[i] + '-none'; 
1559                 }
1560                 
1561                 
1562                 return;
1563             }
1564             cfg.cls += ' col-' + size + '-' + settings[size] + (
1565                 size == 'xs' ? (' col-' + settings[size] ) : '' // bs4 col-{num} replaces col-xs
1566             );
1567             
1568         });
1569         
1570         if (this.hidden) {
1571             cfg.cls += ' hidden';
1572         }
1573         
1574         if (this.alert && ["success","info","warning", "danger"].indexOf(this.alert) > -1) {
1575             cfg.cls +=' alert alert-' + this.alert;
1576         }
1577         
1578         
1579         if (this.html.length) {
1580             cfg.html = this.html;
1581         }
1582         if (this.fa) {
1583             var fasize = '';
1584             if (this.fasize > 1) {
1585                 fasize = ' fa-' + this.fasize + 'x';
1586             }
1587             cfg.html = '<i class="fa fa-'+this.fa + fasize + '"></i>' + (cfg.html || '');
1588             
1589             
1590         }
1591         if (this.icon) {
1592             cfg.html = '<i class="glyphicon glyphicon-'+this.icon + '"></i>' +  (cfg.html || '');
1593         }
1594         
1595         return cfg;
1596     }
1597    
1598 });
1599
1600  
1601
1602  /*
1603  * - LGPL
1604  *
1605  * page container.
1606  * 
1607  */
1608
1609
1610 /**
1611  * @class Roo.bootstrap.Container
1612  * @extends Roo.bootstrap.Component
1613  * @children Roo.bootstrap.Component
1614  * @parent builder
1615  * Bootstrap Container class
1616  * @cfg {Boolean} jumbotron is it a jumbotron element
1617  * @cfg {String} html content of element
1618  * @cfg {String} well (lg|sm|md) a well, large, small or medium.
1619  * @cfg {String} panel (default|primary|success|info|warning|danger) render as panel  - type - primary/success.....
1620  * @cfg {String} header content of header (for panel)
1621  * @cfg {String} footer content of footer (for panel)
1622  * @cfg {String} sticky (footer|wrap|push) block to use as footer or body- needs css-bootstrap/sticky-footer.css
1623  * @cfg {String} tag (header|aside|section) type of HTML tag.
1624  * @cfg {String} alert (success|info|warning|danger) type alert (changes background / border...)
1625  * @cfg {String} fa font awesome icon
1626  * @cfg {String} icon (info-sign|check|...) glyphicon name
1627  * @cfg {Boolean} hidden (true|false) hide the element
1628  * @cfg {Boolean} expandable (true|false) default false
1629  * @cfg {Boolean} expanded (true|false) default true
1630  * @cfg {String} rheader contet on the right of header
1631  * @cfg {Boolean} clickable (true|false) default false
1632
1633  *     
1634  * @constructor
1635  * Create a new Container
1636  * @param {Object} config The config object
1637  */
1638
1639 Roo.bootstrap.Container = function(config){
1640     Roo.bootstrap.Container.superclass.constructor.call(this, config);
1641     
1642     this.addEvents({
1643         // raw events
1644          /**
1645          * @event expand
1646          * After the panel has been expand
1647          * 
1648          * @param {Roo.bootstrap.Container} this
1649          */
1650         "expand" : true,
1651         /**
1652          * @event collapse
1653          * After the panel has been collapsed
1654          * 
1655          * @param {Roo.bootstrap.Container} this
1656          */
1657         "collapse" : true,
1658         /**
1659          * @event click
1660          * When a element is chick
1661          * @param {Roo.bootstrap.Container} this
1662          * @param {Roo.EventObject} e
1663          */
1664         "click" : true
1665     });
1666 };
1667
1668 Roo.extend(Roo.bootstrap.Container, Roo.bootstrap.Component,  {
1669     
1670     jumbotron : false,
1671     well: '',
1672     panel : '',
1673     header: '',
1674     footer : '',
1675     sticky: '',
1676     tag : false,
1677     alert : false,
1678     fa: false,
1679     icon : false,
1680     expandable : false,
1681     rheader : '',
1682     expanded : true,
1683     clickable: false,
1684   
1685      
1686     getChildContainer : function() {
1687         
1688         if(!this.el){
1689             return false;
1690         }
1691         
1692         if (this.panel.length) {
1693             return this.el.select('.panel-body',true).first();
1694         }
1695         
1696         return this.el;
1697     },
1698     
1699     
1700     getAutoCreate : function(){
1701         
1702         var cfg = {
1703             tag : this.tag || 'div',
1704             html : '',
1705             cls : ''
1706         };
1707         if (this.jumbotron) {
1708             cfg.cls = 'jumbotron';
1709         }
1710         
1711         
1712         
1713         // - this is applied by the parent..
1714         //if (this.cls) {
1715         //    cfg.cls = this.cls + '';
1716         //}
1717         
1718         if (this.sticky.length) {
1719             
1720             var bd = Roo.get(document.body);
1721             if (!bd.hasClass('bootstrap-sticky')) {
1722                 bd.addClass('bootstrap-sticky');
1723                 Roo.select('html',true).setStyle('height', '100%');
1724             }
1725              
1726             cfg.cls += 'bootstrap-sticky-' + this.sticky;
1727         }
1728         
1729         
1730         if (this.well.length) {
1731             switch (this.well) {
1732                 case 'lg':
1733                 case 'sm':
1734                     cfg.cls +=' well well-' +this.well;
1735                     break;
1736                 default:
1737                     cfg.cls +=' well';
1738                     break;
1739             }
1740         }
1741         
1742         if (this.hidden) {
1743             cfg.cls += ' hidden';
1744         }
1745         
1746         
1747         if (this.alert && ["success","info","warning", "danger"].indexOf(this.alert) > -1) {
1748             cfg.cls +=' alert alert-' + this.alert;
1749         }
1750         
1751         var body = cfg;
1752         
1753         if (this.panel.length) {
1754             cfg.cls += ' panel panel-' + this.panel;
1755             cfg.cn = [];
1756             if (this.header.length) {
1757                 
1758                 var h = [];
1759                 
1760                 if(this.expandable){
1761                     
1762                     cfg.cls = cfg.cls + ' expandable';
1763                     
1764                     h.push({
1765                         tag: 'i',
1766                         cls: (this.expanded ? 'fa fa-minus' : 'fa fa-plus') 
1767                     });
1768                     
1769                 }
1770                 
1771                 h.push(
1772                     {
1773                         tag: 'span',
1774                         cls : 'panel-title',
1775                         html : (this.expandable ? '&nbsp;' : '') + this.header
1776                     },
1777                     {
1778                         tag: 'span',
1779                         cls: 'panel-header-right',
1780                         html: this.rheader
1781                     }
1782                 );
1783                 
1784                 cfg.cn.push({
1785                     cls : 'panel-heading',
1786                     style : this.expandable ? 'cursor: pointer' : '',
1787                     cn : h
1788                 });
1789                 
1790             }
1791             
1792             body = false;
1793             cfg.cn.push({
1794                 cls : 'panel-body' + (this.expanded ? '' : ' hide'),
1795                 html : this.html
1796             });
1797             
1798             
1799             if (this.footer.length) {
1800                 cfg.cn.push({
1801                     cls : 'panel-footer',
1802                     html : this.footer
1803                     
1804                 });
1805             }
1806             
1807         }
1808         
1809         if (body) {
1810             body.html = this.html || cfg.html;
1811             // prefix with the icons..
1812             if (this.fa) {
1813                 body.html = '<i class="fa fa-'+this.fa + '"></i>' + body.html ;
1814             }
1815             if (this.icon) {
1816                 body.html = '<i class="glyphicon glyphicon-'+this.icon + '"></i>' + body.html ;
1817             }
1818             
1819             
1820         }
1821         if ((!this.cls || !this.cls.length) && (!cfg.cls || !cfg.cls.length)) {
1822             cfg.cls =  'container';
1823         }
1824         
1825         return cfg;
1826     },
1827     
1828     initEvents: function() 
1829     {
1830         if(this.expandable){
1831             var headerEl = this.headerEl();
1832         
1833             if(headerEl){
1834                 headerEl.on('click', this.onToggleClick, this);
1835             }
1836         }
1837         
1838         if(this.clickable){
1839             this.el.on('click', this.onClick, this);
1840         }
1841         
1842     },
1843     
1844     onToggleClick : function()
1845     {
1846         var headerEl = this.headerEl();
1847         
1848         if(!headerEl){
1849             return;
1850         }
1851         
1852         if(this.expanded){
1853             this.collapse();
1854             return;
1855         }
1856         
1857         this.expand();
1858     },
1859     
1860     expand : function()
1861     {
1862         if(this.fireEvent('expand', this)) {
1863             
1864             this.expanded = true;
1865             
1866             //this.el.select('.panel-body',true).first().setVisibilityMode(Roo.Element.DISPLAY).show();
1867             
1868             this.el.select('.panel-body',true).first().removeClass('hide');
1869             
1870             var toggleEl = this.toggleEl();
1871
1872             if(!toggleEl){
1873                 return;
1874             }
1875
1876             toggleEl.removeClass(['fa-minus', 'fa-plus']).addClass(['fa-minus']);
1877         }
1878         
1879     },
1880     
1881     collapse : function()
1882     {
1883         if(this.fireEvent('collapse', this)) {
1884             
1885             this.expanded = false;
1886             
1887             //this.el.select('.panel-body',true).first().setVisibilityMode(Roo.Element.DISPLAY).hide();
1888             this.el.select('.panel-body',true).first().addClass('hide');
1889         
1890             var toggleEl = this.toggleEl();
1891
1892             if(!toggleEl){
1893                 return;
1894             }
1895
1896             toggleEl.removeClass(['fa-minus', 'fa-plus']).addClass(['fa-plus']);
1897         }
1898     },
1899     
1900     toggleEl : function()
1901     {
1902         if(!this.el || !this.panel.length || !this.header.length || !this.expandable){
1903             return;
1904         }
1905         
1906         return this.el.select('.panel-heading .fa',true).first();
1907     },
1908     
1909     headerEl : function()
1910     {
1911         if(!this.el || !this.panel.length || !this.header.length){
1912             return;
1913         }
1914         
1915         return this.el.select('.panel-heading',true).first()
1916     },
1917     
1918     bodyEl : function()
1919     {
1920         if(!this.el || !this.panel.length){
1921             return;
1922         }
1923         
1924         return this.el.select('.panel-body',true).first()
1925     },
1926     
1927     titleEl : function()
1928     {
1929         if(!this.el || !this.panel.length || !this.header.length){
1930             return;
1931         }
1932         
1933         return this.el.select('.panel-title',true).first();
1934     },
1935     
1936     setTitle : function(v)
1937     {
1938         var titleEl = this.titleEl();
1939         
1940         if(!titleEl){
1941             return;
1942         }
1943         
1944         titleEl.dom.innerHTML = v;
1945     },
1946     
1947     getTitle : function()
1948     {
1949         
1950         var titleEl = this.titleEl();
1951         
1952         if(!titleEl){
1953             return '';
1954         }
1955         
1956         return titleEl.dom.innerHTML;
1957     },
1958     
1959     setRightTitle : function(v)
1960     {
1961         var t = this.el.select('.panel-header-right',true).first();
1962         
1963         if(!t){
1964             return;
1965         }
1966         
1967         t.dom.innerHTML = v;
1968     },
1969     
1970     onClick : function(e)
1971     {
1972         e.preventDefault();
1973         
1974         this.fireEvent('click', this, e);
1975     }
1976 });
1977
1978  /**
1979  * @class Roo.bootstrap.Card
1980  * @extends Roo.bootstrap.Component
1981  * @children Roo.bootstrap.Component
1982  * @licence LGPL
1983  * Bootstrap Card class - note this has children as CardHeader/ImageTop/Footer.. - which should really be listed properties?
1984  *
1985  *
1986  * possible... may not be implemented..
1987  * @cfg {String} header_image  src url of image.
1988  * @cfg {String|Object} header
1989  * @cfg {Number} header_size (0|1|2|3|4|5) H1 or H2 etc.. 0 indicates default
1990  * @cfg {Number} header_weight  (primary|secondary|success|info|warning|danger|light|dark)
1991  * 
1992  * @cfg {String} title
1993  * @cfg {String} subtitle
1994  * @cfg {String|Boolean} html -- html contents - or just use children.. use false to hide it..
1995  * @cfg {String} footer
1996  
1997  * @cfg {String} weight (primary|warning|info|danger|secondary|success|light|dark)
1998  * 
1999  * @cfg {String} margin (0|1|2|3|4|5|auto)
2000  * @cfg {String} margin_top (0|1|2|3|4|5|auto)
2001  * @cfg {String} margin_bottom (0|1|2|3|4|5|auto)
2002  * @cfg {String} margin_left (0|1|2|3|4|5|auto)
2003  * @cfg {String} margin_right (0|1|2|3|4|5|auto)
2004  * @cfg {String} margin_x (0|1|2|3|4|5|auto)
2005  * @cfg {String} margin_y (0|1|2|3|4|5|auto)
2006  *
2007  * @cfg {String} padding (0|1|2|3|4|5)
2008  * @cfg {String} padding_top (0|1|2|3|4|5)next_to_card
2009  * @cfg {String} padding_bottom (0|1|2|3|4|5)
2010  * @cfg {String} padding_left (0|1|2|3|4|5)
2011  * @cfg {String} padding_right (0|1|2|3|4|5)
2012  * @cfg {String} padding_x (0|1|2|3|4|5)
2013  * @cfg {String} padding_y (0|1|2|3|4|5)
2014  *
2015  * @cfg {String} display (none|inline|inline-block|block|table|table-cell|table-row|flex|inline-flex)
2016  * @cfg {String} display_xs (none|inline|inline-block|block|table|table-cell|table-row|flex|inline-flex)
2017  * @cfg {String} display_sm (none|inline|inline-block|block|table|table-cell|table-row|flex|inline-flex)
2018  * @cfg {String} display_lg (none|inline|inline-block|block|table|table-cell|table-row|flex|inline-flex)
2019  * @cfg {String} display_xl (none|inline|inline-block|block|table|table-cell|table-row|flex|inline-flex)
2020  
2021  * @config {Boolean} dragable  if this card can be dragged.
2022  * @config {String} drag_group  group for drag
2023  * @config {Boolean} dropable  if this card can recieve other cards being dropped onto it..
2024  * @config {String} drop_group  group for drag
2025  * 
2026  * @config {Boolean} collapsable can the body be collapsed.
2027  * @config {Boolean} collapsed is the body collapsed when rendered...
2028  * @config {Boolean} rotateable can the body be rotated by clicking on it..
2029  * @config {Boolean} rotated is the body rotated when rendered...
2030  * 
2031  * @constructor
2032  * Create a new Container
2033  * @param {Object} config The config object
2034  */
2035
2036 Roo.bootstrap.Card = function(config){
2037     Roo.bootstrap.Card.superclass.constructor.call(this, config);
2038     
2039     this.addEvents({
2040          // raw events
2041         /**
2042          * @event drop
2043          * When a element a card is dropped
2044          * @param {Roo.bootstrap.Card} this
2045          *
2046          * 
2047          * @param {Roo.bootstrap.Card} move_card the card being dropped?
2048          * @param {String} position 'above' or 'below'
2049          * @param {Roo.bootstrap.Card} next_to_card What card position is relative to of 'false' for empty list.
2050         
2051          */
2052         'drop' : true,
2053          /**
2054          * @event rotate
2055          * When a element a card is rotate
2056          * @param {Roo.bootstrap.Card} this
2057          * @param {Roo.Element} n the node being dropped?
2058          * @param {Boolean} rotate status
2059          */
2060         'rotate' : true,
2061         /**
2062          * @event cardover
2063          * When a card element is dragged over ready to drop (return false to block dropable)
2064          * @param {Roo.bootstrap.Card} this
2065          * @param {Object} data from dragdrop 
2066          */
2067          'cardover' : true
2068          
2069     });
2070 };
2071
2072
2073 Roo.extend(Roo.bootstrap.Card, Roo.bootstrap.Component,  {
2074     
2075     
2076     weight : '',
2077     
2078     margin: '', /// may be better in component?
2079     margin_top: '', 
2080     margin_bottom: '', 
2081     margin_left: '',
2082     margin_right: '',
2083     margin_x: '',
2084     margin_y: '',
2085     
2086     padding : '',
2087     padding_top: '', 
2088     padding_bottom: '', 
2089     padding_left: '',
2090     padding_right: '',
2091     padding_x: '',
2092     padding_y: '',
2093     
2094     display: '', 
2095     display_xs: '', 
2096     display_sm: '', 
2097     display_lg: '',
2098     display_xl: '',
2099  
2100     header_image  : '',
2101     header : '',
2102     header_size : 0,
2103     title : '',
2104     subtitle : '',
2105     html : '',
2106     footer: '',
2107
2108     collapsable : false,
2109     collapsed : false,
2110     rotateable : false,
2111     rotated : false,
2112     
2113     dragable : false,
2114     drag_group : false,
2115     dropable : false,
2116     drop_group : false,
2117     childContainer : false,
2118     dropEl : false, /// the dom placeholde element that indicates drop location.
2119     containerEl: false, // body container
2120     bodyEl: false, // card-body
2121     headerContainerEl : false, //
2122     headerEl : false,
2123     header_imageEl : false,
2124     
2125     
2126     layoutCls : function()
2127     {
2128         var cls = '';
2129         var t = this;
2130         Roo.log(this.margin_bottom.length);
2131         ['', 'top', 'bottom', 'left', 'right', 'x', 'y' ].forEach(function(v) {
2132             // in theory these can do margin_top : ml-xs-3 ??? but we don't support that yet
2133             
2134             if (('' + t['margin' + (v.length ? '_' : '') + v]).length) {
2135                 cls += ' m' +  (v.length ? v[0]  : '') + '-' +  t['margin' + (v.length ? '_' : '') + v];
2136             }
2137             if (('' + t['padding' + (v.length ? '_' : '') + v]).length) {
2138                 cls += ' p' +  (v.length ? v[0]  : '') + '-' +  t['padding' + (v.length ? '_' : '') + v];
2139             }
2140         });
2141         
2142         ['', 'xs', 'sm', 'lg', 'xl'].forEach(function(v) {
2143             if (('' + t['display' + (v.length ? '_' : '') + v]).length) {
2144                 cls += ' d' +  (v.length ? '-' : '') + v + '-' + t['display' + (v.length ? '_' : '') + v]
2145             }
2146         });
2147         
2148         // more generic support?
2149         if (this.hidden) {
2150             cls += ' d-none';
2151         }
2152         
2153         return cls;
2154     },
2155  
2156        // Roo.log("Call onRender: " + this.xtype);
2157         /*  We are looking at something like this.
2158 <div class="card">
2159     <img src="..." class="card-img-top" alt="...">
2160     <div class="card-body">
2161         <h5 class="card-title">Card title</h5>
2162          <h6 class="card-subtitle mb-2 text-muted">Card subtitle</h6>
2163
2164         >> this bit is really the body...
2165         <div> << we will ad dthis in hopefully it will not break shit.
2166         
2167         ** card text does not actually have any styling...
2168         
2169             <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>
2170         
2171         </div> <<
2172           <a href="#" class="card-link">Card link</a>
2173           
2174     </div>
2175     <div class="card-footer">
2176         <small class="text-muted">Last updated 3 mins ago</small>
2177     </div>
2178 </div>
2179          */
2180     getAutoCreate : function(){
2181         
2182         var cfg = {
2183             tag : 'div',
2184             cls : 'card',
2185             cn : [ ]
2186         };
2187         
2188         if (this.weight.length && this.weight != 'light') {
2189             cfg.cls += ' text-white';
2190         } else {
2191             cfg.cls += ' text-dark'; // need as it's nested..
2192         }
2193         if (this.weight.length) {
2194             cfg.cls += ' bg-' + this.weight;
2195         }
2196         
2197         cfg.cls += ' ' + this.layoutCls(); 
2198         
2199         var hdr = false;
2200         var hdr_ctr = false;
2201         if (this.header.length) {
2202             hdr = {
2203                 tag : this.header_size > 0 ? 'h' + this.header_size : 'div',
2204                 cls : 'card-header ' + (this.header_weight ? 'bg-' + this.header_weight : ''),
2205                 cn : []
2206             };
2207             cfg.cn.push(hdr);
2208             hdr_ctr = hdr;
2209         } else {
2210             hdr = {
2211                 tag : 'div',
2212                 cls : 'card-header d-none ' + (this.header_weight ? 'bg-' + this.header_weight : ''),
2213                 cn : []
2214             };
2215             cfg.cn.push(hdr);
2216             hdr_ctr = hdr;
2217         }
2218         if (this.collapsable) {
2219             hdr_ctr = {
2220             tag : 'a',
2221             cls : 'd-block user-select-none',
2222             cn: [
2223                     {
2224                         tag: 'i',
2225                         cls : 'roo-collapse-toggle fa fa-chevron-down float-right ' + (this.collapsed ? 'collapsed' : '')
2226                     }
2227                    
2228                 ]
2229             };
2230             hdr.cn.push(hdr_ctr);
2231         }
2232         
2233         hdr_ctr.cn.push(        {
2234             tag: 'span',
2235             cls: 'roo-card-header-ctr' + ( this.header.length ? '' : ' d-none'),
2236             html : this.header
2237         });
2238         
2239         
2240         if (this.header_image.length) {
2241             cfg.cn.push({
2242                 tag : 'img',
2243                 cls : 'card-img-top',
2244                 src: this.header_image // escape?
2245             });
2246         } else {
2247             cfg.cn.push({
2248                     tag : 'div',
2249                     cls : 'card-img-top d-none' 
2250                 });
2251         }
2252             
2253         var body = {
2254             tag : 'div',
2255             cls : 'card-body' + (this.html === false  ? ' d-none' : ''),
2256             cn : []
2257         };
2258         var obody = body;
2259         if (this.collapsable || this.rotateable) {
2260             obody = {
2261                 tag: 'div',
2262                 cls : 'roo-collapsable collapse ' + (this.collapsed || this.rotated ? '' : 'show'),
2263                 cn : [  body ]
2264             };
2265         }
2266         
2267         cfg.cn.push(obody);
2268         
2269         if (this.title.length) {
2270             body.cn.push({
2271                 tag : 'div',
2272                 cls : 'card-title',
2273                 src: this.title // escape?
2274             });
2275         }  
2276         
2277         if (this.subtitle.length) {
2278             body.cn.push({
2279                 tag : 'div',
2280                 cls : 'card-title',
2281                 src: this.subtitle // escape?
2282             });
2283         }
2284         
2285         body.cn.push({
2286             tag : 'div',
2287             cls : 'roo-card-body-ctr'
2288         });
2289         
2290         if (this.html.length) {
2291             body.cn.push({
2292                 tag: 'div',
2293                 html : this.html
2294             });
2295         }
2296         // fixme ? handle objects?
2297         
2298         if (this.footer.length) {
2299            
2300             cfg.cn.push({
2301                 cls : 'card-footer ' + (this.rotated ? 'd-none' : ''),
2302                 html : this.footer
2303             });
2304             
2305         } else {
2306             cfg.cn.push({cls : 'card-footer d-none'});
2307         }
2308         
2309         // footer...
2310         
2311         return cfg;
2312     },
2313     
2314     
2315     getCardHeader : function()
2316     {
2317         var  ret = this.el.select('.card-header',true).first();
2318         if (ret.hasClass('d-none')) {
2319             ret.removeClass('d-none');
2320         }
2321         
2322         return ret;
2323     },
2324     getCardFooter : function()
2325     {
2326         var  ret = this.el.select('.card-footer',true).first();
2327         if (ret.hasClass('d-none')) {
2328             ret.removeClass('d-none');
2329         }
2330         
2331         return ret;
2332     },
2333     getCardImageTop : function()
2334     {
2335         var  ret = this.header_imageEl;
2336         if (ret.hasClass('d-none')) {
2337             ret.removeClass('d-none');
2338         }
2339             
2340         return ret;
2341     },
2342     
2343     getChildContainer : function()
2344     {
2345         
2346         if(!this.el){
2347             return false;
2348         }
2349         return this.el.select('.roo-card-body-ctr',true).first();    
2350     },
2351     
2352     initEvents: function() 
2353     {
2354         this.bodyEl = this.el.select('.card-body',true).first(); 
2355         this.containerEl = this.getChildContainer();
2356         if(this.dragable){
2357             this.dragZone = new Roo.dd.DragZone(this.getEl(), {
2358                     containerScroll: true,
2359                     ddGroup: this.drag_group || 'default_card_drag_group'
2360             });
2361             this.dragZone.getDragData = this.getDragData.createDelegate(this);
2362         }
2363         if (this.dropable) {
2364             this.dropZone = new Roo.dd.DropZone(this.el.select('.card-body',true).first() , {
2365                 containerScroll: true,
2366                 ddGroup: this.drop_group || 'default_card_drag_group'
2367             });
2368             this.dropZone.getTargetFromEvent = this.getTargetFromEvent.createDelegate(this);
2369             this.dropZone.onNodeEnter = this.onNodeEnter.createDelegate(this);
2370             this.dropZone.onNodeOver = this.onNodeOver.createDelegate(this);
2371             this.dropZone.onNodeOut = this.onNodeOut.createDelegate(this);
2372             this.dropZone.onNodeDrop = this.onNodeDrop.createDelegate(this);
2373         }
2374         
2375         if (this.collapsable) {
2376             this.el.select('.card-header',true).on('click', this.onToggleCollapse, this);
2377         }
2378         if (this.rotateable) {
2379             this.el.select('.card-header',true).on('click', this.onToggleRotate, this);
2380         }
2381         this.collapsableEl = this.el.select('.roo-collapsable',true).first();
2382          
2383         this.footerEl = this.el.select('.card-footer',true).first();
2384         this.collapsableToggleEl = this.el.select('.roo-collapse-toggle',true).first();
2385         this.headerContainerEl = this.el.select('.roo-card-header-ctr',true).first();
2386         this.headerEl = this.el.select('.card-header',true).first();
2387         
2388         if (this.rotated) {
2389             this.el.addClass('roo-card-rotated');
2390             this.fireEvent('rotate', this, true);
2391         }
2392         this.header_imageEl = this.el.select('.card-img-top',true).first(); 
2393         this.header_imageEl.on('load', this.onHeaderImageLoad, this );
2394         
2395     },
2396     getDragData : function(e)
2397     {
2398         var target = this.getEl();
2399         if (target) {
2400             //this.handleSelection(e);
2401             
2402             var dragData = {
2403                 source: this,
2404                 copy: false,
2405                 nodes: this.getEl(),
2406                 records: []
2407             };
2408             
2409             
2410             dragData.ddel = target.dom ;    // the div element
2411             Roo.log(target.getWidth( ));
2412             dragData.ddel.style.width = target.getWidth() + 'px';
2413             
2414             return dragData;
2415         }
2416         return false;
2417     },
2418     /**
2419     *    Part of the Roo.dd.DropZone interface. If no target node is found, the
2420     *    whole Element becomes the target, and this causes the drop gesture to append.
2421     *
2422     *    Returns an object:
2423     *     {
2424            
2425            position : 'below' or 'above'
2426            card  : relateive to card OBJECT (or true for no cards listed)
2427            items_n : relative to nth item in list
2428            card_n : relative to  nth card in list
2429     }
2430     *
2431     *    
2432     */
2433     getTargetFromEvent : function(e, dragged_card_el)
2434     {
2435         var target = e.getTarget();
2436         while ((target !== null) && (target.parentNode != this.containerEl.dom)) {
2437             target = target.parentNode;
2438         }
2439         
2440         var ret = {
2441             position: '',
2442             cards : [],
2443             card_n : -1,
2444             items_n : -1,
2445             card : false 
2446         };
2447         
2448         //Roo.log([ 'target' , target ? target.id : '--nothing--']);
2449         // see if target is one of the 'cards'...
2450         
2451         
2452         //Roo.log(this.items.length);
2453         var pos = false;
2454         
2455         var last_card_n = 0;
2456         var cards_len  = 0;
2457         for (var i = 0;i< this.items.length;i++) {
2458             
2459             if (!this.items[i].el.hasClass('card')) {
2460                  continue;
2461             }
2462             pos = this.getDropPoint(e, this.items[i].el.dom);
2463             
2464             cards_len = ret.cards.length;
2465             //Roo.log(this.items[i].el.dom.id);
2466             ret.cards.push(this.items[i]);
2467             last_card_n  = i;
2468             if (ret.card_n < 0 && pos == 'above') {
2469                 ret.position = cards_len > 0 ? 'below' : pos;
2470                 ret.items_n = i > 0 ? i - 1 : 0;
2471                 ret.card_n  = cards_len  > 0 ? cards_len - 1 : 0;
2472                 ret.card = ret.cards[ret.card_n];
2473             }
2474         }
2475         if (!ret.cards.length) {
2476             ret.card = true;
2477             ret.position = 'below';
2478             ret.items_n;
2479             return ret;
2480         }
2481         // could not find a card.. stick it at the end..
2482         if (ret.card_n < 0) {
2483             ret.card_n = last_card_n;
2484             ret.card = ret.cards[last_card_n];
2485             ret.items_n = this.items.indexOf(ret.cards[last_card_n]);
2486             ret.position = 'below';
2487         }
2488         
2489         if (this.items[ret.items_n].el == dragged_card_el) {
2490             return false;
2491         }
2492         
2493         if (ret.position == 'below') {
2494             var card_after = ret.card_n+1 == ret.cards.length ? false : ret.cards[ret.card_n+1];
2495             
2496             if (card_after  && card_after.el == dragged_card_el) {
2497                 return false;
2498             }
2499             return ret;
2500         }
2501         
2502         // its's after ..
2503         var card_before = ret.card_n > 0 ? ret.cards[ret.card_n-1] : false;
2504         
2505         if (card_before  && card_before.el == dragged_card_el) {
2506             return false;
2507         }
2508         
2509         return ret;
2510     },
2511     
2512     onNodeEnter : function(n, dd, e, data){
2513         return false;
2514     },
2515     onNodeOver : function(n, dd, e, data)
2516     {
2517        
2518         var target_info = this.getTargetFromEvent(e,data.source.el);
2519         if (target_info === false) {
2520             this.dropPlaceHolder('hide');
2521             return false;
2522         }
2523         Roo.log(['getTargetFromEvent', target_info ]);
2524         
2525         
2526         if (this.fireEvent('cardover', this, [ data ]) === false) {
2527             return false;
2528         }
2529         
2530         this.dropPlaceHolder('show', target_info,data);
2531         
2532         return false; 
2533     },
2534     onNodeOut : function(n, dd, e, data){
2535         this.dropPlaceHolder('hide');
2536      
2537     },
2538     onNodeDrop : function(n, dd, e, data)
2539     {
2540         
2541         // call drop - return false if
2542         
2543         // this could actually fail - if the Network drops..
2544         // we will ignore this at present..- client should probably reload
2545         // the whole set of cards if stuff like that fails.
2546         
2547         
2548         var info = this.getTargetFromEvent(e,data.source.el);
2549         if (info === false) {
2550             return false;
2551         }
2552         this.dropPlaceHolder('hide');
2553   
2554           
2555     
2556         this.acceptCard(data.source, info.position, info.card, info.items_n);
2557         return true;
2558          
2559     },
2560     firstChildCard : function()
2561     {
2562         for (var i = 0;i< this.items.length;i++) {
2563             
2564             if (!this.items[i].el.hasClass('card')) {
2565                  continue;
2566             }
2567             return this.items[i];
2568         }
2569         return this.items.length ? this.items[this.items.length-1] : false; // don't try and put stuff after the cards...
2570     },
2571     /**
2572      * accept card
2573      *
2574      * -        card.acceptCard(move_card, info.position, info.card, info.items_n);
2575      */
2576     acceptCard : function(move_card,  position, next_to_card )
2577     {
2578         if (this.fireEvent("drop", this, move_card, position, next_to_card) === false) {
2579             return false;
2580         }
2581         
2582         var to_items_n = next_to_card ? this.items.indexOf(next_to_card) : 0;
2583         
2584         move_card.parent().removeCard(move_card);
2585         
2586         
2587         var dom = move_card.el.dom;
2588         dom.style.width = ''; // clear with - which is set by drag.
2589         
2590         if (next_to_card !== false && next_to_card !== true && next_to_card.el.dom.parentNode) {
2591             var cardel = next_to_card.el.dom;
2592             
2593             if (position == 'above' ) {
2594                 cardel.parentNode.insertBefore(dom, cardel);
2595             } else if (cardel.nextSibling) {
2596                 cardel.parentNode.insertBefore(dom,cardel.nextSibling);
2597             } else {
2598                 cardel.parentNode.append(dom);
2599             }
2600         } else {
2601             // card container???
2602             this.containerEl.dom.append(dom);
2603         }
2604         
2605         //FIXME HANDLE card = true 
2606         
2607         // add this to the correct place in items.
2608         
2609         // remove Card from items.
2610         
2611        
2612         if (this.items.length) {
2613             var nitems = [];
2614             //Roo.log([info.items_n, info.position, this.items.length]);
2615             for (var i =0; i < this.items.length; i++) {
2616                 if (i == to_items_n && position == 'above') {
2617                     nitems.push(move_card);
2618                 }
2619                 nitems.push(this.items[i]);
2620                 if (i == to_items_n && position == 'below') {
2621                     nitems.push(move_card);
2622                 }
2623             }
2624             this.items = nitems;
2625             Roo.log(this.items);
2626         } else {
2627             this.items.push(move_card);
2628         }
2629         
2630         move_card.parentId = this.id;
2631         
2632         return true;
2633         
2634         
2635     },
2636     removeCard : function(c)
2637     {
2638         this.items = this.items.filter(function(e) { return e != c });
2639  
2640         var dom = c.el.dom;
2641         dom.parentNode.removeChild(dom);
2642         dom.style.width = ''; // clear with - which is set by drag.
2643         c.parentId = false;
2644         
2645     },
2646     
2647     /**    Decide whether to drop above or below a View node. */
2648     getDropPoint : function(e, n, dd)
2649     {
2650         if (dd) {
2651              return false;
2652         }
2653         if (n == this.containerEl.dom) {
2654             return "above";
2655         }
2656         var t = Roo.lib.Dom.getY(n), b = t + n.offsetHeight;
2657         var c = t + (b - t) / 2;
2658         var y = Roo.lib.Event.getPageY(e);
2659         if(y <= c) {
2660             return "above";
2661         }else{
2662             return "below";
2663         }
2664     },
2665     onToggleCollapse : function(e)
2666         {
2667         if (this.collapsed) {
2668             this.el.select('.roo-collapse-toggle').removeClass('collapsed');
2669             this.collapsableEl.addClass('show');
2670             this.collapsed = false;
2671             return;
2672         }
2673         this.el.select('.roo-collapse-toggle').addClass('collapsed');
2674         this.collapsableEl.removeClass('show');
2675         this.collapsed = true;
2676         
2677     
2678     },
2679     
2680     onToggleRotate : function(e)
2681     {
2682         this.collapsableEl.removeClass('show');
2683         this.footerEl.removeClass('d-none');
2684         this.el.removeClass('roo-card-rotated');
2685         this.el.removeClass('d-none');
2686         if (this.rotated) {
2687             
2688             this.collapsableEl.addClass('show');
2689             this.rotated = false;
2690             this.fireEvent('rotate', this, this.rotated);
2691             return;
2692         }
2693         this.el.addClass('roo-card-rotated');
2694         this.footerEl.addClass('d-none');
2695         this.el.select('.roo-collapsable').removeClass('show');
2696         
2697         this.rotated = true;
2698         this.fireEvent('rotate', this, this.rotated);
2699     
2700     },
2701     
2702     dropPlaceHolder: function (action, info, data)
2703     {
2704         if (this.dropEl === false) {
2705             this.dropEl = Roo.DomHelper.append(this.containerEl, {
2706             cls : 'd-none'
2707             },true);
2708         }
2709         this.dropEl.removeClass(['d-none', 'd-block']);        
2710         if (action == 'hide') {
2711             
2712             this.dropEl.addClass('d-none');
2713             return;
2714         }
2715         // FIXME - info.card == true!!!
2716         this.dropEl.dom.parentNode.removeChild(this.dropEl.dom);
2717         
2718         if (info.card !== true) {
2719             var cardel = info.card.el.dom;
2720             
2721             if (info.position == 'above') {
2722                 cardel.parentNode.insertBefore(this.dropEl.dom, cardel);
2723             } else if (cardel.nextSibling) {
2724                 cardel.parentNode.insertBefore(this.dropEl.dom,cardel.nextSibling);
2725             } else {
2726                 cardel.parentNode.append(this.dropEl.dom);
2727             }
2728         } else {
2729             // card container???
2730             this.containerEl.dom.append(this.dropEl.dom);
2731         }
2732         
2733         this.dropEl.addClass('d-block roo-card-dropzone');
2734         
2735         this.dropEl.setHeight( Roo.get(data.ddel).getHeight() );
2736         
2737         
2738     
2739     
2740     
2741     },
2742     setHeaderText: function(html)
2743     {
2744         this.header = html;
2745         if (this.headerContainerEl) {
2746             this.headerContainerEl.dom.innerHTML = html;
2747         }
2748     },
2749     onHeaderImageLoad : function(ev, he)
2750     {
2751         if (!this.header_image_fit_square) {
2752             return;
2753         }
2754         
2755         var hw = he.naturalHeight / he.naturalWidth;
2756         // wide image = < 0
2757         // tall image = > 1
2758         //var w = he.dom.naturalWidth;
2759         var ww = he.width;
2760         he.style.left =  0;
2761         he.style.position =  'relative';
2762         if (hw > 1) {
2763             var nw = (ww * (1/hw));
2764             Roo.get(he).setSize( ww * (1/hw),  ww);
2765             he.style.left =  ((ww - nw)/ 2) + 'px';
2766             he.style.position =  'relative';
2767         }
2768
2769     }
2770
2771     
2772 });
2773
2774 /*
2775  * - LGPL
2776  *
2777  * Card header - holder for the card header elements.
2778  * 
2779  */
2780
2781 /**
2782  * @class Roo.bootstrap.CardHeader
2783  * @extends Roo.bootstrap.Element
2784  * @parent Roo.bootstrap.Card
2785  * @children Roo.bootstrap.Component
2786  * Bootstrap CardHeader class
2787  * @constructor
2788  * Create a new Card Header - that you can embed children into
2789  * @param {Object} config The config object
2790  */
2791
2792 Roo.bootstrap.CardHeader = function(config){
2793     Roo.bootstrap.CardHeader.superclass.constructor.call(this, config);
2794 };
2795
2796 Roo.extend(Roo.bootstrap.CardHeader, Roo.bootstrap.Element,  {
2797     
2798     
2799     container_method : 'getCardHeader' 
2800     
2801      
2802     
2803     
2804    
2805 });
2806
2807  
2808
2809  /*
2810  * - LGPL
2811  *
2812  * Card footer - holder for the card footer elements.
2813  * 
2814  */
2815
2816 /**
2817  * @class Roo.bootstrap.CardFooter
2818  * @extends Roo.bootstrap.Element
2819  * @parent Roo.bootstrap.Card
2820  * @children Roo.bootstrap.Component
2821  * Bootstrap CardFooter class
2822  * 
2823  * @constructor
2824  * Create a new Card Footer - that you can embed children into
2825  * @param {Object} config The config object
2826  */
2827
2828 Roo.bootstrap.CardFooter = function(config){
2829     Roo.bootstrap.CardFooter.superclass.constructor.call(this, config);
2830 };
2831
2832 Roo.extend(Roo.bootstrap.CardFooter, Roo.bootstrap.Element,  {
2833     
2834     
2835     container_method : 'getCardFooter' 
2836     
2837      
2838     
2839     
2840    
2841 });
2842
2843  
2844
2845  /*
2846  * - LGPL
2847  *
2848  * Card header - holder for the card header elements.
2849  * 
2850  */
2851
2852 /**
2853  * @class Roo.bootstrap.CardImageTop
2854  * @extends Roo.bootstrap.Element
2855  * @parent Roo.bootstrap.Card
2856  * @children Roo.bootstrap.Component
2857  * Bootstrap CardImageTop class
2858  * 
2859  * @constructor
2860  * Create a new Card Image Top container
2861  * @param {Object} config The config object
2862  */
2863
2864 Roo.bootstrap.CardImageTop = function(config){
2865     Roo.bootstrap.CardImageTop.superclass.constructor.call(this, config);
2866 };
2867
2868 Roo.extend(Roo.bootstrap.CardImageTop, Roo.bootstrap.Element,  {
2869     
2870    
2871     container_method : 'getCardImageTop' 
2872     
2873      
2874     
2875    
2876 });
2877
2878  
2879
2880  
2881 /*
2882 * Licence: LGPL
2883 */
2884
2885 /**
2886  * @class Roo.bootstrap.ButtonUploader
2887  * @extends Roo.bootstrap.Button
2888  * Bootstrap Button Uploader class - it's a button which when you add files to it
2889  *
2890  * 
2891  * @cfg {Number} errorTimeout default 3000
2892  * @cfg {Array}  images  an array of ?? Img objects ??? when loading existing files..
2893  * @cfg {Array}  html The button text.
2894  * @cfg {Boolean}  multiple (default true) Should the upload allow multiple files to be uploaded.
2895  *
2896  * @constructor
2897  * Create a new CardUploader
2898  * @param {Object} config The config object
2899  */
2900
2901 Roo.bootstrap.ButtonUploader = function(config){
2902     
2903  
2904     
2905     Roo.bootstrap.ButtonUploader.superclass.constructor.call(this, config);
2906     
2907      
2908      this.addEvents({
2909          // raw events
2910         /**
2911          * @event beforeselect
2912          * When button is pressed, before show upload files dialog is shown
2913          * @param {Roo.bootstrap.UploaderButton} this
2914          *
2915          */
2916         'beforeselect' : true,
2917          /**
2918          * @event fired when files have been selected, 
2919          * When a the download link is clicked
2920          * @param {Roo.bootstrap.UploaderButton} this
2921          * @param {Array} Array of files that have been uploaded
2922          */
2923         'uploaded' : true
2924         
2925     });
2926 };
2927  
2928 Roo.extend(Roo.bootstrap.ButtonUploader, Roo.bootstrap.Button,  {
2929     
2930      
2931     errorTimeout : 3000,
2932      
2933     images : false,
2934    
2935     fileCollection : false,
2936     allowBlank : true,
2937     
2938     multiple : true,
2939     
2940     getAutoCreate : function()
2941     {
2942        
2943         
2944         return  {
2945             cls :'div' ,
2946             cn : [
2947                 Roo.bootstrap.Button.prototype.getAutoCreate.call(this) 
2948             ]
2949         };
2950            
2951          
2952     },
2953      
2954    
2955     initEvents : function()
2956     {
2957         
2958         Roo.bootstrap.Button.prototype.initEvents.call(this);
2959         
2960         
2961         
2962         
2963         
2964         this.urlAPI = (window.createObjectURL && window) || 
2965                                 (window.URL && URL.revokeObjectURL && URL) || 
2966                                 (window.webkitURL && webkitURL);
2967                         
2968         var im = {
2969             tag: 'input',
2970             type : 'file',
2971             cls : 'd-none  roo-card-upload-selector' 
2972           
2973         };
2974         if (this.multiple) {
2975             im.multiple = 'multiple';
2976         }
2977         this.selectorEl = Roo.get(document.body).createChild(im); // so it does not capture click event for navitem.
2978        
2979         //this.selectorEl = this.el.select('.roo-card-upload-selector', true).first();
2980         
2981         this.selectorEl.on('change', this.onFileSelected, this);
2982          
2983          
2984        
2985     },
2986     
2987    
2988     onClick : function(e)
2989     {
2990         e.preventDefault();
2991         
2992         if ( this.fireEvent('beforeselect', this) === false) {
2993             return;
2994         }
2995          
2996         this.selectorEl.dom.click();
2997          
2998     },
2999     
3000     onFileSelected : function(e)
3001     {
3002         e.preventDefault();
3003         
3004         if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
3005             return;
3006         }
3007         var files = Array.prototype.slice.call(this.selectorEl.dom.files);
3008         this.selectorEl.dom.value  = '';// hopefully reset..
3009         
3010         this.fireEvent('uploaded', this,  files );
3011         
3012     },
3013     
3014        
3015    
3016     
3017     /**
3018      * addCard - add an Attachment to the uploader
3019      * @param data - the data about the image to upload
3020      *
3021      * {
3022           id : 123
3023           title : "Title of file",
3024           is_uploaded : false,
3025           src : "http://.....",
3026           srcfile : { the File upload object },
3027           mimetype : file.type,
3028           preview : false,
3029           is_deleted : 0
3030           .. any other data...
3031         }
3032      *
3033      * 
3034     */
3035      
3036     reset: function()
3037     {
3038          
3039          this.selectorEl
3040     } 
3041     
3042     
3043     
3044     
3045 });
3046  /*
3047  * - LGPL
3048  *
3049  * image
3050  * 
3051  */
3052
3053
3054 /**
3055  * @class Roo.bootstrap.Img
3056  * @extends Roo.bootstrap.Component
3057  * Bootstrap Img class
3058  * @cfg {Boolean} imgResponsive false | true
3059  * @cfg {String} border rounded | circle | thumbnail
3060  * @cfg {String} src image source
3061  * @cfg {String} alt image alternative text
3062  * @cfg {String} href a tag href
3063  * @cfg {String} target (_self|_blank|_parent|_top)target for a href.
3064  * @cfg {String} xsUrl xs image source
3065  * @cfg {String} smUrl sm image source
3066  * @cfg {String} mdUrl md image source
3067  * @cfg {String} lgUrl lg image source
3068  * @cfg {Boolean} backgroundContain (use style background and contain image in content)
3069  * 
3070  * @constructor
3071  * Create a new Input
3072  * @param {Object} config The config object
3073  */
3074
3075 Roo.bootstrap.Img = function(config){
3076     Roo.bootstrap.Img.superclass.constructor.call(this, config);
3077     
3078     this.addEvents({
3079         // img events
3080         /**
3081          * @event click
3082          * The img click event for the img.
3083          * @param {Roo.EventObject} e
3084          */
3085         "click" : true,
3086         /**
3087          * @event load
3088          * The when any image loads
3089          * @param {Roo.EventObject} e
3090          */
3091         "load" : true
3092     });
3093 };
3094
3095 Roo.extend(Roo.bootstrap.Img, Roo.bootstrap.Component,  {
3096     
3097     imgResponsive: true,
3098     border: '',
3099     src: 'about:blank',
3100     href: false,
3101     target: false,
3102     xsUrl: '',
3103     smUrl: '',
3104     mdUrl: '',
3105     lgUrl: '',
3106     backgroundContain : false,
3107
3108     getAutoCreate : function()
3109     {   
3110         if(this.src || (!this.xsUrl && !this.smUrl && !this.mdUrl && !this.lgUrl)){
3111             return this.createSingleImg();
3112         }
3113         
3114         var cfg = {
3115             tag: 'div',
3116             cls: 'roo-image-responsive-group',
3117             cn: []
3118         };
3119         var _this = this;
3120         
3121         Roo.each(['xs', 'sm', 'md', 'lg'], function(size){
3122             
3123             if(!_this[size + 'Url']){
3124                 return;
3125             }
3126             
3127             var img = {
3128                 tag: 'img',
3129                 cls: (_this.imgResponsive) ? 'img-responsive' : '',
3130                 html: _this.html || cfg.html,
3131                 src: _this[size + 'Url']
3132             };
3133             
3134             img.cls += ' roo-image-responsive-' + size;
3135             
3136             var s = ['xs', 'sm', 'md', 'lg'];
3137             
3138             s.splice(s.indexOf(size), 1);
3139             
3140             Roo.each(s, function(ss){
3141                 img.cls += ' hidden-' + ss;
3142             });
3143             
3144             if (['rounded','circle','thumbnail'].indexOf(_this.border)>-1) {
3145                 cfg.cls += ' img-' + _this.border;
3146             }
3147             
3148             if(_this.alt){
3149                 cfg.alt = _this.alt;
3150             }
3151             
3152             if(_this.href){
3153                 var a = {
3154                     tag: 'a',
3155                     href: _this.href,
3156                     cn: [
3157                         img
3158                     ]
3159                 };
3160
3161                 if(this.target){
3162                     a.target = _this.target;
3163                 }
3164             }
3165             
3166             cfg.cn.push((_this.href) ? a : img);
3167             
3168         });
3169         
3170         return cfg;
3171     },
3172     
3173     createSingleImg : function()
3174     {
3175         var cfg = {
3176             tag: 'img',
3177             cls: (this.imgResponsive) ? 'img-responsive' : '',
3178             html : null,
3179             src : Roo.BLANK_IMAGE_URL  // just incase src get's set to undefined?!?
3180         };
3181         
3182         if (this.backgroundContain) {
3183             cfg.cls += ' background-contain';
3184         }
3185         
3186         cfg.html = this.html || cfg.html;
3187         
3188         if (this.backgroundContain) {
3189             cfg.style="background-image: url(" + this.src + ')';
3190         } else {
3191             cfg.src = this.src || cfg.src;
3192         }
3193         
3194         if (['rounded','circle','thumbnail'].indexOf(this.border)>-1) {
3195             cfg.cls += ' img-' + this.border;
3196         }
3197         
3198         if(this.alt){
3199             cfg.alt = this.alt;
3200         }
3201         
3202         if(this.href){
3203             var a = {
3204                 tag: 'a',
3205                 href: this.href,
3206                 cn: [
3207                     cfg
3208                 ]
3209             };
3210             
3211             if(this.target){
3212                 a.target = this.target;
3213             }
3214             
3215         }
3216         
3217         return (this.href) ? a : cfg;
3218     },
3219     
3220     initEvents: function() 
3221     {
3222         if(!this.href){
3223             this.el.on('click', this.onClick, this);
3224         }
3225         if(this.src || (!this.xsUrl && !this.smUrl && !this.mdUrl && !this.lgUrl)){
3226             this.el.on('load', this.onImageLoad, this);
3227         } else {
3228             // not sure if this works.. not tested
3229             this.el.select('img', true).on('load', this.onImageLoad, this);
3230         }
3231         
3232     },
3233     
3234     onClick : function(e)
3235     {
3236         Roo.log('img onclick');
3237         this.fireEvent('click', this, e);
3238     },
3239     onImageLoad: function(e)
3240     {
3241         Roo.log('img load');
3242         this.fireEvent('load', this, e);
3243     },
3244     
3245     /**
3246      * Sets the url of the image - used to update it
3247      * @param {String} url the url of the image
3248      */
3249     
3250     setSrc : function(url)
3251     {
3252         this.src =  url;
3253         
3254         if(this.src || (!this.xsUrl && !this.smUrl && !this.mdUrl && !this.lgUrl)){
3255             if (this.backgroundContain) {
3256                 this.el.dom.style.backgroundImage =  'url(' + url + ')';
3257             } else {
3258                 this.el.dom.src =  url;
3259             }
3260             return;
3261         }
3262         
3263         this.el.select('img', true).first().dom.src =  url;
3264     }
3265     
3266     
3267    
3268 });
3269
3270  /*
3271  * - LGPL
3272  *
3273  * image
3274  * 
3275  */
3276
3277
3278 /**
3279  * @class Roo.bootstrap.Link
3280  * @extends Roo.bootstrap.Component
3281  * @children Roo.bootstrap.Component
3282  * Bootstrap Link Class (eg. '<a href>')
3283  
3284  * @cfg {String} alt image alternative text
3285  * @cfg {String} href a tag href
3286  * @cfg {String} target (_self|_blank|_parent|_top) target for a href.
3287  * @cfg {String} html the content of the link.
3288  * @cfg {String} anchor name for the anchor link
3289  * @cfg {String} fa - favicon
3290
3291  * @cfg {Boolean} preventDefault (true | false) default false
3292
3293  * 
3294  * @constructor
3295  * Create a new Input
3296  * @param {Object} config The config object
3297  */
3298
3299 Roo.bootstrap.Link = function(config){
3300     Roo.bootstrap.Link.superclass.constructor.call(this, config);
3301     
3302     this.addEvents({
3303         // img events
3304         /**
3305          * @event click
3306          * The img click event for the img.
3307          * @param {Roo.EventObject} e
3308          */
3309         "click" : true
3310     });
3311 };
3312
3313 Roo.extend(Roo.bootstrap.Link, Roo.bootstrap.Component,  {
3314     
3315     href: false,
3316     target: false,
3317     preventDefault: false,
3318     anchor : false,
3319     alt : false,
3320     fa: false,
3321
3322
3323     getAutoCreate : function()
3324     {
3325         var html = this.html || '';
3326         
3327         if (this.fa !== false) {
3328             html = '<i class="fa fa-' + this.fa + '"></i>';
3329         }
3330         var cfg = {
3331             tag: 'a'
3332         };
3333         // anchor's do not require html/href...
3334         if (this.anchor === false) {
3335             cfg.html = html;
3336             cfg.href = this.href || '#';
3337         } else {
3338             cfg.name = this.anchor;
3339             if (this.html !== false || this.fa !== false) {
3340                 cfg.html = html;
3341             }
3342             if (this.href !== false) {
3343                 cfg.href = this.href;
3344             }
3345         }
3346         
3347         if(this.alt !== false){
3348             cfg.alt = this.alt;
3349         }
3350         
3351         
3352         if(this.target !== false) {
3353             cfg.target = this.target;
3354         }
3355         
3356         return cfg;
3357     },
3358     
3359     initEvents: function() {
3360         
3361         if(!this.href || this.preventDefault){
3362             this.el.on('click', this.onClick, this);
3363         }
3364     },
3365     
3366     onClick : function(e)
3367     {
3368         if(this.preventDefault){
3369             e.preventDefault();
3370         }
3371         //Roo.log('img onclick');
3372         this.fireEvent('click', this, e);
3373     }
3374    
3375 });
3376
3377  /*
3378  * - LGPL
3379  *
3380  * header
3381  * 
3382  */
3383
3384 /**
3385  * @class Roo.bootstrap.Header
3386  * @extends Roo.bootstrap.Component
3387  * @children Roo.bootstrap.Component
3388  * Bootstrap Header class
3389  *
3390  * 
3391  * @cfg {String} html content of header
3392  * @cfg {Number} level (1|2|3|4|5|6) default 1
3393  * 
3394  * @constructor
3395  * Create a new Header
3396  * @param {Object} config The config object
3397  */
3398
3399
3400 Roo.bootstrap.Header  = function(config){
3401     Roo.bootstrap.Header.superclass.constructor.call(this, config);
3402 };
3403
3404 Roo.extend(Roo.bootstrap.Header, Roo.bootstrap.Component,  {
3405     
3406     //href : false,
3407     html : false,
3408     level : 1,
3409     
3410     
3411     
3412     getAutoCreate : function(){
3413         
3414         
3415         
3416         var cfg = {
3417             tag: 'h' + (1 *this.level),
3418             html: this.html || ''
3419         } ;
3420         
3421         return cfg;
3422     }
3423    
3424 });
3425
3426  
3427
3428  /**
3429  * @class Roo.bootstrap.MenuMgr
3430  * @licence LGPL
3431  * Provides a common registry of all menu items on a page so that they can be easily accessed by id.
3432  * @static
3433  */
3434 Roo.bootstrap.menu.Manager = function(){
3435    var menus, active, groups = {}, attached = false, lastShow = new Date();
3436
3437    // private - called when first menu is created
3438    function init(){
3439        menus = {};
3440        active = new Roo.util.MixedCollection();
3441        Roo.get(document).addKeyListener(27, function(){
3442            if(active.length > 0){
3443                hideAll();
3444            }
3445        });
3446    }
3447
3448    // private
3449    function hideAll(){
3450        if(active && active.length > 0){
3451            var c = active.clone();
3452            c.each(function(m){
3453                m.hide();
3454            });
3455        }
3456    }
3457
3458    // private
3459    function onHide(m){
3460        active.remove(m);
3461        if(active.length < 1){
3462            Roo.get(document).un("mouseup", onMouseDown);
3463             
3464            attached = false;
3465        }
3466    }
3467
3468    // private
3469    function onShow(m){
3470        var last = active.last();
3471        lastShow = new Date();
3472        active.add(m);
3473        if(!attached){
3474           Roo.get(document).on("mouseup", onMouseDown);
3475            
3476            attached = true;
3477        }
3478        if(m.parentMenu){
3479           //m.getEl().setZIndex(parseInt(m.parentMenu.getEl().getStyle("z-index"), 10) + 3);
3480           m.parentMenu.activeChild = m;
3481        }else if(last && last.isVisible()){
3482           //m.getEl().setZIndex(parseInt(last.getEl().getStyle("z-index"), 10) + 3);
3483        }
3484    }
3485
3486    // private
3487    function onBeforeHide(m){
3488        if(m.activeChild){
3489            m.activeChild.hide();
3490        }
3491        if(m.autoHideTimer){
3492            clearTimeout(m.autoHideTimer);
3493            delete m.autoHideTimer;
3494        }
3495    }
3496
3497    // private
3498    function onBeforeShow(m){
3499        var pm = m.parentMenu;
3500        if(!pm && !m.allowOtherMenus){
3501            hideAll();
3502        }else if(pm && pm.activeChild && active != m){
3503            pm.activeChild.hide();
3504        }
3505    }
3506
3507    // private this should really trigger on mouseup..
3508    function onMouseDown(e){
3509         Roo.log("on Mouse Up");
3510         
3511         if(lastShow.getElapsed() > 50 && active.length > 0 && !e.getTarget(".dropdown-menu") && !e.getTarget('.user-menu')){
3512             Roo.log("MenuManager hideAll");
3513             hideAll();
3514             e.stopEvent();
3515         }
3516         
3517         
3518    }
3519
3520    // private
3521    function onBeforeCheck(mi, state){
3522        if(state){
3523            var g = groups[mi.group];
3524            for(var i = 0, l = g.length; i < l; i++){
3525                if(g[i] != mi){
3526                    g[i].setChecked(false);
3527                }
3528            }
3529        }
3530    }
3531
3532    return {
3533
3534        /**
3535         * Hides all menus that are currently visible
3536         */
3537        hideAll : function(){
3538             hideAll();  
3539        },
3540
3541        // private
3542        register : function(menu){
3543            if(!menus){
3544                init();
3545            }
3546            menus[menu.id] = menu;
3547            menu.on("beforehide", onBeforeHide);
3548            menu.on("hide", onHide);
3549            menu.on("beforeshow", onBeforeShow);
3550            menu.on("show", onShow);
3551            var g = menu.group;
3552            if(g && menu.events["checkchange"]){
3553                if(!groups[g]){
3554                    groups[g] = [];
3555                }
3556                groups[g].push(menu);
3557                menu.on("checkchange", onCheck);
3558            }
3559        },
3560
3561         /**
3562          * Returns a {@link Roo.menu.Menu} object
3563          * @param {String/Object} menu The string menu id, an existing menu object reference, or a Menu config that will
3564          * be used to generate and return a new Menu instance.
3565          */
3566        get : function(menu){
3567            if(typeof menu == "string"){ // menu id
3568                return menus[menu];
3569            }else if(menu.events){  // menu instance
3570                return menu;
3571            }
3572            /*else if(typeof menu.length == 'number'){ // array of menu items?
3573                return new Roo.bootstrap.Menu({items:menu});
3574            }else{ // otherwise, must be a config
3575                return new Roo.bootstrap.Menu(menu);
3576            }
3577            */
3578            return false;
3579        },
3580
3581        // private
3582        unregister : function(menu){
3583            delete menus[menu.id];
3584            menu.un("beforehide", onBeforeHide);
3585            menu.un("hide", onHide);
3586            menu.un("beforeshow", onBeforeShow);
3587            menu.un("show", onShow);
3588            var g = menu.group;
3589            if(g && menu.events["checkchange"]){
3590                groups[g].remove(menu);
3591                menu.un("checkchange", onCheck);
3592            }
3593        },
3594
3595        // private
3596        registerCheckable : function(menuItem){
3597            var g = menuItem.group;
3598            if(g){
3599                if(!groups[g]){
3600                    groups[g] = [];
3601                }
3602                groups[g].push(menuItem);
3603                menuItem.on("beforecheckchange", onBeforeCheck);
3604            }
3605        },
3606
3607        // private
3608        unregisterCheckable : function(menuItem){
3609            var g = menuItem.group;
3610            if(g){
3611                groups[g].remove(menuItem);
3612                menuItem.un("beforecheckchange", onBeforeCheck);
3613            }
3614        }
3615    };
3616 }(); 
3617 /**
3618  * @class Roo.bootstrap.menu.Menu
3619  * @extends Roo.bootstrap.Component
3620  * @licence LGPL
3621  * @children Roo.bootstrap.menu.Item Roo.bootstrap.menu.Separator
3622  * @parent none
3623  * Bootstrap Menu class - container for MenuItems - normally has to be added to a object that supports the menu property
3624  * 
3625  * @cfg {String} type (dropdown|treeview|submenu) type of menu
3626  * @cfg {bool} hidden  if the menu should be hidden when rendered.
3627  * @cfg {bool} stopEvent (true|false)  Stop event after trigger press (default true)
3628  * @cfg {bool} isLink (true|false)  the menu has link disable auto expand and collaspe (default false)
3629 * @cfg {bool} hideTrigger (true|false)  default false - hide the carret for trigger.
3630 * @cfg {String} align  default tl-bl? == below  - how the menu should be aligned. 
3631  
3632  * @constructor
3633  * Create a new Menu
3634  * @param {Object} config The config objectQ
3635  */
3636
3637
3638 Roo.bootstrap.menu.Menu = function(config){
3639     
3640     if (config.type == 'treeview') {
3641         // normally menu's are drawn attached to the document to handle layering etc..
3642         // however treeview (used by the docs menu is drawn into the parent element)
3643         this.container_method = 'getChildContainer'; 
3644     }
3645     
3646     Roo.bootstrap.menu.Menu.superclass.constructor.call(this, config);
3647     if (this.registerMenu && this.type != 'treeview')  {
3648         Roo.bootstrap.menu.Manager.register(this);
3649     }
3650     
3651     
3652     this.addEvents({
3653         /**
3654          * @event beforeshow
3655          * Fires before this menu is displayed (return false to block)
3656          * @param {Roo.menu.Menu} this
3657          */
3658         beforeshow : true,
3659         /**
3660          * @event beforehide
3661          * Fires before this menu is hidden (return false to block)
3662          * @param {Roo.menu.Menu} this
3663          */
3664         beforehide : true,
3665         /**
3666          * @event show
3667          * Fires after this menu is displayed
3668          * @param {Roo.menu.Menu} this
3669          */
3670         show : true,
3671         /**
3672          * @event hide
3673          * Fires after this menu is hidden
3674          * @param {Roo.menu.Menu} this
3675          */
3676         hide : true,
3677         /**
3678          * @event click
3679          * Fires when this menu is clicked (or when the enter key is pressed while it is active)
3680          * @param {Roo.menu.Menu} this
3681          * @param {Roo.menu.Item} menuItem The menu item that was clicked
3682          * @param {Roo.EventObject} e
3683          */
3684         click : true,
3685         /**
3686          * @event mouseover
3687          * Fires when the mouse is hovering over this menu
3688          * @param {Roo.menu.Menu} this
3689          * @param {Roo.EventObject} e
3690          * @param {Roo.menu.Item} menuItem The menu item that was clicked
3691          */
3692         mouseover : true,
3693         /**
3694          * @event mouseout
3695          * Fires when the mouse exits this menu
3696          * @param {Roo.menu.Menu} this
3697          * @param {Roo.EventObject} e
3698          * @param {Roo.menu.Item} menuItem The menu item that was clicked
3699          */
3700         mouseout : true,
3701         /**
3702          * @event itemclick
3703          * Fires when a menu item contained in this menu is clicked
3704          * @param {Roo.menu.BaseItem} baseItem The BaseItem that was clicked
3705          * @param {Roo.EventObject} e
3706          */
3707         itemclick: true
3708     });
3709     this.menuitems = new Roo.util.MixedCollection(false, function(o) { return o.el.id; });
3710 };
3711
3712 Roo.extend(Roo.bootstrap.menu.Menu, Roo.bootstrap.Component,  {
3713     
3714    /// html : false,
3715    
3716     triggerEl : false,  // is this set by component builder? -- it should really be fetched from parent()???
3717     type: false,
3718     /**
3719      * @cfg {Boolean} registerMenu True (default) - means that clicking on screen etc. hides it.
3720      */
3721     registerMenu : true,
3722     
3723     menuItems :false, // stores the menu items..
3724     
3725     hidden:true,
3726         
3727     parentMenu : false,
3728     
3729     stopEvent : true,
3730     
3731     isLink : false,
3732     
3733     container_method : 'getDocumentBody', // so the menu is rendered on the body and zIndex works.
3734     
3735     hideTrigger : false,
3736     
3737     align : 'tl-bl?',
3738     
3739     
3740     getChildContainer : function() {
3741         return this.el;  
3742     },
3743     
3744     getAutoCreate : function(){
3745          
3746         //if (['right'].indexOf(this.align)!==-1) {
3747         //    cfg.cn[1].cls += ' pull-right'
3748         //}
3749          
3750         var cfg = {
3751             tag : 'ul',
3752             cls : 'dropdown-menu shadow' ,
3753             style : 'z-index:1000'
3754             
3755         };
3756         
3757         if (this.type === 'submenu') {
3758             cfg.cls = 'submenu active';
3759         }
3760         if (this.type === 'treeview') {
3761             cfg.cls = 'treeview-menu';
3762         }
3763         
3764         return cfg;
3765     },
3766     initEvents : function() {
3767         
3768        // Roo.log("ADD event");
3769        // Roo.log(this.triggerEl.dom);
3770         if (this.triggerEl) {
3771             
3772             this.triggerEl.on('click', this.onTriggerClick, this);
3773             
3774             this.triggerEl.on(Roo.isTouch ? 'touchstart' : 'mouseup', this.onTriggerPress, this);
3775             
3776             if (!this.hideTrigger) {
3777                 if (this.triggerEl.hasClass('nav-item') && this.triggerEl.select('.nav-link',true).length) {
3778                     // dropdown toggle on the 'a' in BS4?
3779                     this.triggerEl.select('.nav-link',true).first().addClass('dropdown-toggle');
3780                 } else {
3781                     this.triggerEl.addClass('dropdown-toggle');
3782                 }
3783             }
3784         }
3785         
3786         if (Roo.isTouch) {
3787             this.el.on('touchstart'  , this.onTouch, this);
3788         }
3789         this.el.on('click' , this.onClick, this);
3790
3791         this.el.on("mouseover", this.onMouseOver, this);
3792         this.el.on("mouseout", this.onMouseOut, this);
3793         
3794     },
3795     
3796     findTargetItem : function(e)
3797     {
3798         var t = e.getTarget(".dropdown-menu-item", this.el,  true);
3799         if(!t){
3800             return false;
3801         }
3802         //Roo.log(t);         Roo.log(t.id);
3803         if(t && t.id){
3804             //Roo.log(this.menuitems);
3805             return this.menuitems.get(t.id);
3806             
3807             //return this.items.get(t.menuItemId);
3808         }
3809         
3810         return false;
3811     },
3812     
3813     onTouch : function(e) 
3814     {
3815         Roo.log("menu.onTouch");
3816         //e.stopEvent(); this make the user popdown broken
3817         this.onClick(e);
3818     },
3819     
3820     onClick : function(e)
3821     {
3822         Roo.log("menu.onClick");
3823         
3824         var t = this.findTargetItem(e);
3825         if(!t || t.isContainer){
3826             return;
3827         }
3828         Roo.log(e);
3829         /*
3830         if (Roo.isTouch && e.type == 'touchstart' && t.menu  && !t.disabled) {
3831             if(t == this.activeItem && t.shouldDeactivate(e)){
3832                 this.activeItem.deactivate();
3833                 delete this.activeItem;
3834                 return;
3835             }
3836             if(t.canActivate){
3837                 this.setActiveItem(t, true);
3838             }
3839             return;
3840             
3841             
3842         }
3843         */
3844        
3845         Roo.log('pass click event');
3846         
3847         t.onClick(e);
3848         
3849         this.fireEvent("click", this, t, e);
3850         
3851         var _this = this;
3852         
3853         if(!t.href.length || t.href == '#'){
3854             (function() { _this.hide(); }).defer(100);
3855         }
3856         
3857     },
3858     
3859     onMouseOver : function(e){
3860         var t  = this.findTargetItem(e);
3861         //Roo.log(t);
3862         //if(t){
3863         //    if(t.canActivate && !t.disabled){
3864         //        this.setActiveItem(t, true);
3865         //    }
3866         //}
3867         
3868         this.fireEvent("mouseover", this, e, t);
3869     },
3870     isVisible : function(){
3871         return !this.hidden;
3872     },
3873     onMouseOut : function(e){
3874         var t  = this.findTargetItem(e);
3875         
3876         //if(t ){
3877         //    if(t == this.activeItem && t.shouldDeactivate(e)){
3878         //        this.activeItem.deactivate();
3879         //        delete this.activeItem;
3880         //    }
3881         //}
3882         this.fireEvent("mouseout", this, e, t);
3883     },
3884     
3885     
3886     /**
3887      * Displays this menu relative to another element
3888      * @param {String/HTMLElement/Roo.Element} element The element to align to
3889      * @param {String} position (optional) The {@link Roo.Element#alignTo} anchor position to use in aligning to
3890      * the element (defaults to this.defaultAlign)
3891      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
3892      */
3893     show : function(el, pos, parentMenu)
3894     {
3895         if (false === this.fireEvent("beforeshow", this)) {
3896             Roo.log("show canceled");
3897             return;
3898         }
3899         this.parentMenu = parentMenu;
3900         if(!this.el){
3901             this.render();
3902         }
3903         this.el.addClass('show'); // show otherwise we do not know how big we are..
3904          
3905         var xy = this.el.getAlignToXY(el, pos);
3906         
3907         // bl-tl << left align  below
3908         // tl-bl << left align 
3909         
3910         if(this.el.getWidth() + xy[0] >= Roo.lib.Dom.getViewWidth()){
3911             // if it goes to far to the right.. -> align left.
3912             xy = this.el.getAlignToXY(el, this.align.replace('/l/g', 'r'))
3913         }
3914         if(xy[0] < 0){
3915             // was left align - go right?
3916             xy = this.el.getAlignToXY(el, this.align.replace('/r/g', 'l'))
3917         }
3918         
3919         // goes down the bottom
3920         if(this.el.getHeight() + xy[1] >= Roo.lib.Dom.getViewHeight() ||
3921            xy[1]  < 0 ){
3922             var a = this.align.replace('?', '').split('-');
3923             xy = this.el.getAlignToXY(el, a[1]  + '-' + a[0] + '?')
3924             
3925         }
3926         
3927         this.showAt(  xy , parentMenu, false);
3928     },
3929      /**
3930      * Displays this menu at a specific xy position
3931      * @param {Array} xyPosition Contains X & Y [x, y] values for the position at which to show the menu (coordinates are page-based)
3932      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
3933      */
3934     showAt : function(xy, parentMenu, /* private: */_e){
3935         this.parentMenu = parentMenu;
3936         if(!this.el){
3937             this.render();
3938         }
3939         if(_e !== false){
3940             this.fireEvent("beforeshow", this);
3941             //xy = this.el.adjustForConstraints(xy);
3942         }
3943         
3944         //this.el.show();
3945         this.hideMenuItems();
3946         this.hidden = false;
3947         if (this.triggerEl) {
3948             this.triggerEl.addClass('open');
3949         }
3950         
3951         this.el.addClass('show');
3952         
3953         
3954         
3955         // reassign x when hitting right
3956         
3957         // reassign y when hitting bottom
3958         
3959         // but the list may align on trigger left or trigger top... should it be a properity?
3960         
3961         if(this.el.getStyle('top') != 'auto' && this.el.getStyle('top').slice(-1) != "%"){
3962             this.el.setXY(xy);
3963         }
3964         
3965         this.focus();
3966         this.fireEvent("show", this);
3967     },
3968     
3969     focus : function(){
3970         return;
3971         if(!this.hidden){
3972             this.doFocus.defer(50, this);
3973         }
3974     },
3975
3976     doFocus : function(){
3977         if(!this.hidden){
3978             this.focusEl.focus();
3979         }
3980     },
3981
3982     /**
3983      * Hides this menu and optionally all parent menus
3984      * @param {Boolean} deep (optional) True to hide all parent menus recursively, if any (defaults to false)
3985      */
3986     hide : function(deep)
3987     {
3988         if (false === this.fireEvent("beforehide", this)) {
3989             Roo.log("hide canceled");
3990             return;
3991         }
3992         this.hideMenuItems();
3993         if(this.el && this.isVisible()){
3994            
3995             if(this.activeItem){
3996                 this.activeItem.deactivate();
3997                 this.activeItem = null;
3998             }
3999             if (this.triggerEl) {
4000                 this.triggerEl.removeClass('open');
4001             }
4002             
4003             this.el.removeClass('show');
4004             this.hidden = true;
4005             this.fireEvent("hide", this);
4006         }
4007         if(deep === true && this.parentMenu){
4008             this.parentMenu.hide(true);
4009         }
4010     },
4011     
4012     onTriggerClick : function(e)
4013     {
4014         Roo.log('trigger click');
4015         
4016         var target = e.getTarget();
4017         
4018         Roo.log(target.nodeName.toLowerCase());
4019         
4020         if(target.nodeName.toLowerCase() === 'i'){
4021             e.preventDefault();
4022         }
4023         
4024     },
4025     
4026     onTriggerPress  : function(e)
4027     {
4028         Roo.log('trigger press');
4029         //Roo.log(e.getTarget());
4030        // Roo.log(this.triggerEl.dom);
4031        
4032         // trigger only occurs on normal menu's -- if it's a treeview or dropdown... do not hide/show..
4033         var pel = Roo.get(e.getTarget());
4034         if (pel.findParent('.dropdown-menu') || pel.findParent('.treeview-menu') ) {
4035             Roo.log('is treeview or dropdown?');
4036             return;
4037         }
4038         
4039         if(e.getTarget().nodeName.toLowerCase() !== 'i' && this.isLink){
4040             return;
4041         }
4042         
4043         if (this.isVisible()) {
4044             Roo.log('hide');
4045             this.hide();
4046         } else {
4047             Roo.log('show');
4048             
4049             this.show(this.triggerEl, this.align, false);
4050         }
4051         
4052         if(this.stopEvent || e.getTarget().nodeName.toLowerCase() === 'i'){
4053             e.stopEvent();
4054         }
4055         
4056     },
4057        
4058     
4059     hideMenuItems : function()
4060     {
4061         Roo.log("hide Menu Items");
4062         if (!this.el) { 
4063             return;
4064         }
4065         
4066         this.el.select('.open',true).each(function(aa) {
4067             
4068             aa.removeClass('open');
4069          
4070         });
4071     },
4072     addxtypeChild : function (tree, cntr) {
4073         var comp= Roo.bootstrap.menu.Menu.superclass.addxtypeChild.call(this, tree, cntr);
4074           
4075         this.menuitems.add(comp);
4076         return comp;
4077
4078     },
4079     getEl : function()
4080     {
4081         Roo.log(this.el);
4082         return this.el;
4083     },
4084     
4085     clear : function()
4086     {
4087         this.getEl().dom.innerHTML = '';
4088         this.menuitems.clear();
4089     }
4090 });
4091
4092  
4093  /**
4094  * @class Roo.bootstrap.menu.Item
4095  * @extends Roo.bootstrap.Component
4096  * @children  Roo.bootstrap.Button Roo.bootstrap.ButtonUploader Roo.bootstrap.Row Roo.bootstrap.Column Roo.bootstrap.Container
4097  * @parent Roo.bootstrap.menu.Menu
4098  * @licence LGPL
4099  * Bootstrap MenuItem class
4100  * 
4101  * @cfg {String} html the menu label
4102  * @cfg {String} href the link
4103  * @cfg {Boolean} preventDefault do not trigger A href on clicks (default false).
4104  * @cfg {Boolean} isContainer is it a container - just returns a drop down item..
4105  * @cfg {Boolean} active  used on sidebars to highlight active itesm
4106  * @cfg {String} fa favicon to show on left of menu item.
4107  * @cfg {Roo.bootsrap.Menu} menu the child menu.
4108  * 
4109  * 
4110  * @constructor
4111  * Create a new MenuItem
4112  * @param {Object} config The config object
4113  */
4114
4115
4116 Roo.bootstrap.menu.Item = function(config){
4117     Roo.bootstrap.menu.Item.superclass.constructor.call(this, config);
4118     this.addEvents({
4119         // raw events
4120         /**
4121          * @event click
4122          * The raw click event for the entire grid.
4123          * @param {Roo.bootstrap.menu.Item} this
4124          * @param {Roo.EventObject} e
4125          */
4126         "click" : true
4127     });
4128 };
4129
4130 Roo.extend(Roo.bootstrap.menu.Item, Roo.bootstrap.Component,  {
4131     
4132     href : false,
4133     html : false,
4134     preventDefault: false,
4135     isContainer : false,
4136     active : false,
4137     fa: false,
4138     
4139     getAutoCreate : function(){
4140         
4141         if(this.isContainer){
4142             return {
4143                 tag: 'li',
4144                 cls: 'dropdown-menu-item '
4145             };
4146         }
4147         var ctag = {
4148             tag: 'span',
4149             html: 'Link'
4150         };
4151         
4152         var anc = {
4153             tag : 'a',
4154             cls : 'dropdown-item',
4155             href : '#',
4156             cn : [  ]
4157         };
4158         
4159         if (this.fa !== false) {
4160             anc.cn.push({
4161                 tag : 'i',
4162                 cls : 'fa fa-' + this.fa
4163             });
4164         }
4165         
4166         anc.cn.push(ctag);
4167         
4168         
4169         var cfg= {
4170             tag: 'li',
4171             cls: 'dropdown-menu-item',
4172             cn: [ anc ]
4173         };
4174         if (this.parent().type == 'treeview') {
4175             cfg.cls = 'treeview-menu';
4176         }
4177         if (this.active) {
4178             cfg.cls += ' active';
4179         }
4180         
4181         
4182         
4183         anc.href = this.href || cfg.cn[0].href ;
4184         ctag.html = this.html || cfg.cn[0].html ;
4185         return cfg;
4186     },
4187     
4188     initEvents: function()
4189     {
4190         if (this.parent().type == 'treeview') {
4191             this.el.select('a').on('click', this.onClick, this);
4192         }
4193         
4194         if (this.menu) {
4195             this.menu.parentType = this.xtype;
4196             this.menu.triggerEl = this.el;
4197             this.menu = this.addxtype(Roo.apply({}, this.menu));
4198         }
4199         
4200     },
4201     onClick : function(e)
4202     {
4203         //Roo.log('item on click ');
4204         
4205         if(this.href === false || this.preventDefault){
4206             e.preventDefault();
4207         }
4208         //this.parent().hideMenuItems();
4209         
4210         this.fireEvent('click', this, e);
4211     },
4212     getEl : function()
4213     {
4214         return this.el;
4215     } 
4216 });
4217
4218  
4219
4220  
4221
4222   
4223 /**
4224  * @class Roo.bootstrap.menu.Separator
4225  * @extends Roo.bootstrap.Component
4226  * @licence LGPL
4227  * @parent Roo.bootstrap.menu.Menu
4228  * Bootstrap Separator class
4229  * 
4230  * @constructor
4231  * Create a new Separator
4232  * @param {Object} config The config object
4233  */
4234
4235
4236 Roo.bootstrap.menu.Separator = function(config){
4237     Roo.bootstrap.menu.Separator.superclass.constructor.call(this, config);
4238 };
4239
4240 Roo.extend(Roo.bootstrap.menu.Separator, Roo.bootstrap.Component,  {
4241     
4242     getAutoCreate : function(){
4243         var cfg = {
4244             tag : 'li',
4245             cls: 'dropdown-divider divider'
4246         };
4247         
4248         return cfg;
4249     }
4250    
4251 });
4252
4253  
4254
4255  
4256 /*
4257 * Licence: LGPL
4258 */
4259
4260 /**
4261  * @class Roo.bootstrap.Modal
4262  * @extends Roo.bootstrap.Component
4263  * @parent none builder
4264  * @children Roo.bootstrap.Component
4265  * Bootstrap Modal class
4266  * @cfg {String} title Title of dialog
4267  * @cfg {String} html - the body of the dialog (for simple ones) - you can also use template..
4268  * @cfg {Roo.Template} tmpl - a template with variables. to use it, add a handler in show:method  adn
4269  * @cfg {Boolean} specificTitle default false
4270  * @cfg {Roo.bootstrap.Button} buttons[] Array of buttons or standard button set..
4271  * @cfg {String} buttonPosition (left|right|center) default right (DEPRICATED) - use mr-auto on buttons to put them on the left
4272  * @cfg {Boolean} animate default true
4273  * @cfg {Boolean} allow_close default true
4274  * @cfg {Boolean} fitwindow default false
4275  * @cfg {Boolean} bodyOverflow should the body element have overflow auto added default false
4276  * @cfg {Number} width fixed width - usefull for chrome extension only really.
4277  * @cfg {Number} height fixed height - usefull for chrome extension only really.
4278  * @cfg {String} size (sm|lg|xl) default empty
4279  * @cfg {Number} max_width set the max width of modal
4280  * @cfg {Boolean} editableTitle can the title be edited
4281
4282  *
4283  *
4284  * @constructor
4285  * Create a new Modal Dialog
4286  * @param {Object} config The config object
4287  */
4288
4289 Roo.bootstrap.Modal = function(config){
4290     Roo.bootstrap.Modal.superclass.constructor.call(this, config);
4291     this.addEvents({
4292         // raw events
4293         /**
4294          * @event btnclick
4295          * The raw btnclick event for the button
4296          * @param {Roo.EventObject} e
4297          */
4298         "btnclick" : true,
4299         /**
4300          * @event resize
4301          * Fire when dialog resize
4302          * @param {Roo.bootstrap.Modal} this
4303          * @param {Roo.EventObject} e
4304          */
4305         "resize" : true,
4306         /**
4307          * @event titlechanged
4308          * Fire when the editable title has been changed
4309          * @param {Roo.bootstrap.Modal} this
4310          * @param {Roo.EventObject} value
4311          */
4312         "titlechanged" : true 
4313         
4314     });
4315     this.buttons = this.buttons || [];
4316
4317     if (this.tmpl) {
4318         this.tmpl = Roo.factory(this.tmpl);
4319     }
4320
4321 };
4322
4323 Roo.extend(Roo.bootstrap.Modal, Roo.bootstrap.Component,  {
4324
4325     title : 'test dialog',
4326
4327     buttons : false,
4328
4329     // set on load...
4330
4331     html: false,
4332
4333     tmp: false,
4334
4335     specificTitle: false,
4336
4337     buttonPosition: 'right',
4338
4339     allow_close : true,
4340
4341     animate : true,
4342
4343     fitwindow: false,
4344     
4345      // private
4346     dialogEl: false,
4347     bodyEl:  false,
4348     footerEl:  false,
4349     titleEl:  false,
4350     closeEl:  false,
4351
4352     size: '',
4353     
4354     max_width: 0,
4355     
4356     max_height: 0,
4357     
4358     fit_content: false,
4359     editableTitle  : false,
4360
4361     onRender : function(ct, position)
4362     {
4363         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
4364
4365         if(!this.el){
4366             var cfg = Roo.apply({},  this.getAutoCreate());
4367             cfg.id = Roo.id();
4368             //if(!cfg.name){
4369             //    cfg.name = typeof(this.name) == 'undefined' ? this.id : this.name;
4370             //}
4371             //if (!cfg.name.length) {
4372             //    delete cfg.name;
4373            // }
4374             if (this.cls) {
4375                 cfg.cls += ' ' + this.cls;
4376             }
4377             if (this.style) {
4378                 cfg.style = this.style;
4379             }
4380             this.el = Roo.get(document.body).createChild(cfg, position);
4381         }
4382         //var type = this.el.dom.type;
4383
4384
4385         if(this.tabIndex !== undefined){
4386             this.el.dom.setAttribute('tabIndex', this.tabIndex);
4387         }
4388
4389         this.dialogEl = this.el.select('.modal-dialog',true).first();
4390         this.bodyEl = this.el.select('.modal-body',true).first();
4391         this.closeEl = this.el.select('.modal-header .close', true).first();
4392         this.headerEl = this.el.select('.modal-header',true).first();
4393         this.titleEl = this.el.select('.modal-title',true).first();
4394         this.footerEl = this.el.select('.modal-footer',true).first();
4395
4396         this.maskEl = Roo.DomHelper.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
4397         
4398         //this.el.addClass("x-dlg-modal");
4399
4400         if (this.buttons.length) {
4401             Roo.each(this.buttons, function(bb) {
4402                 var b = Roo.apply({}, bb);
4403                 b.xns = b.xns || Roo.bootstrap;
4404                 b.xtype = b.xtype || 'Button';
4405                 if (typeof(b.listeners) == 'undefined') {
4406                     b.listeners = { click : this.onButtonClick.createDelegate(this)  };
4407                 }
4408
4409                 var btn = Roo.factory(b);
4410
4411                 btn.render(this.getButtonContainer());
4412
4413             },this);
4414         }
4415         // render the children.
4416         var nitems = [];
4417
4418         if(typeof(this.items) != 'undefined'){
4419             var items = this.items;
4420             delete this.items;
4421
4422             for(var i =0;i < items.length;i++) {
4423                 // we force children not to montor widnow resize  - as we do that for them.
4424                 items[i].monitorWindowResize = false;
4425                 nitems.push(this.addxtype(Roo.apply({}, items[i])));
4426             }
4427         }
4428
4429         this.items = nitems;
4430
4431         // where are these used - they used to be body/close/footer
4432
4433
4434         this.initEvents();
4435         //this.el.addClass([this.fieldClass, this.cls]);
4436
4437     },
4438
4439     getAutoCreate : function()
4440     {
4441         // we will default to modal-body-overflow - might need to remove or make optional later.
4442         var bdy = {
4443                 cls : 'modal-body ' + (this.bodyOverflow ? 'overflow-auto' : ''), 
4444                 html : this.html || ''
4445         };
4446
4447         var title = {
4448             tag: 'h5',
4449             cls : 'modal-title',
4450             html : this.title
4451         };
4452
4453         if(this.specificTitle){ // WTF is this?
4454             title = this.title;
4455         }
4456
4457         var header = [];
4458         if (this.allow_close && Roo.bootstrap.version == 3) {
4459             header.push({
4460                 tag: 'button',
4461                 cls : 'close',
4462                 html : '&times'
4463             });
4464         }
4465
4466         header.push(title);
4467
4468         if (this.editableTitle) {
4469             header.push({
4470                 cls: 'form-control roo-editable-title d-none',
4471                 tag: 'input',
4472                 type: 'text'
4473             });
4474         }
4475         
4476         if (this.allow_close && Roo.bootstrap.version == 4) {
4477             header.push({
4478                 tag: 'button',
4479                 cls : 'close',
4480                 html : '&times'
4481             });
4482         }
4483         
4484         var size = '';
4485
4486         if(this.size.length){
4487             size = 'modal-' + this.size;
4488         }
4489         
4490         var footer = Roo.bootstrap.version == 3 ?
4491             {
4492                 cls : 'modal-footer',
4493                 cn : [
4494                     {
4495                         tag: 'div',
4496                         cls: 'btn-' + this.buttonPosition
4497                     }
4498                 ]
4499
4500             } :
4501             {  // BS4 uses mr-auto on left buttons....
4502                 cls : 'modal-footer'
4503             };
4504
4505             
4506
4507         
4508         
4509         var modal = {
4510             cls: "modal",
4511              cn : [
4512                 {
4513                     cls: "modal-dialog " + size,
4514                     cn : [
4515                         {
4516                             cls : "modal-content",
4517                             cn : [
4518                                 {
4519                                     cls : 'modal-header',
4520                                     cn : header
4521                                 },
4522                                 bdy,
4523                                 footer
4524                             ]
4525
4526                         }
4527                     ]
4528
4529                 }
4530             ]
4531         };
4532
4533         if(this.animate){
4534             modal.cls += ' fade';
4535         }
4536
4537         return modal;
4538
4539     },
4540     getChildContainer : function() {
4541
4542          return this.bodyEl;
4543
4544     },
4545     getButtonContainer : function() {
4546         
4547          return Roo.bootstrap.version == 4 ?
4548             this.el.select('.modal-footer',true).first()
4549             : this.el.select('.modal-footer div',true).first();
4550
4551     },
4552     
4553     closeClick : function()
4554     {
4555         this.hide();
4556     },
4557     
4558     initEvents : function()
4559     {
4560         if (this.allow_close) {
4561             this.closeEl.on('click', this.closeClick, this);
4562         }
4563         Roo.EventManager.onWindowResize(this.resize, this, true);
4564         if (this.editableTitle) {
4565             this.headerEditEl =  this.headerEl.select('.form-control',true).first();
4566             this.headerEl.on('click', function() { this.toggleHeaderInput(true) } , this);
4567             this.headerEditEl.on('keyup', function(e) {
4568                     if([  e.RETURN , e.TAB , e.ESC ].indexOf(e.keyCode) > -1) {
4569                         this.toggleHeaderInput(false)
4570                     }
4571                 }, this);
4572             this.headerEditEl.on('blur', function(e) {
4573                 this.toggleHeaderInput(false)
4574             },this);
4575         }
4576
4577     },
4578   
4579
4580     resize : function()
4581     {
4582         this.maskEl.setSize(
4583             Roo.lib.Dom.getViewWidth(true),
4584             Roo.lib.Dom.getViewHeight(true)
4585         );
4586         
4587         if (this.fitwindow) {
4588             
4589            this.dialogEl.setStyle( { 'max-width' : '100%' });
4590             this.setSize(
4591                 this.width || Roo.lib.Dom.getViewportWidth(true) - 30,
4592                 this.height || Roo.lib.Dom.getViewportHeight(true) // catering margin-top 30 margin-bottom 30
4593             );
4594             return;
4595         }
4596         
4597         if(this.max_width !== 0) {
4598             
4599             var w = Math.min(this.max_width, Roo.lib.Dom.getViewportWidth(true) - 30);
4600             
4601             if(this.height) {
4602                 this.setSize(w, this.height);
4603                 return;
4604             }
4605             
4606             if(this.max_height) {
4607                 this.setSize(w,Math.min(
4608                     this.max_height,
4609                     Roo.lib.Dom.getViewportHeight(true) - 60
4610                 ));
4611                 
4612                 return;
4613             }
4614             
4615             if(!this.fit_content) {
4616                 this.setSize(w, Roo.lib.Dom.getViewportHeight(true) - 60);
4617                 return;
4618             }
4619             
4620             this.setSize(w, Math.min(
4621                 60 +
4622                 this.headerEl.getHeight() + 
4623                 this.footerEl.getHeight() + 
4624                 this.getChildHeight(this.bodyEl.dom.childNodes),
4625                 Roo.lib.Dom.getViewportHeight(true) - 60)
4626             );
4627         }
4628         
4629     },
4630
4631     setSize : function(w,h)
4632     {
4633         if (!w && !h) {
4634             return;
4635         }
4636         
4637         this.resizeTo(w,h);
4638         // any layout/border etc.. resize..
4639         (function () {
4640             this.items.forEach( function(e) {
4641                 e.layout ? e.layout() : false;
4642
4643             });
4644         }).defer(100,this);
4645         
4646     },
4647
4648     show : function() {
4649
4650         if (!this.rendered) {
4651             this.render();
4652         }
4653         this.toggleHeaderInput(false);
4654         //this.el.setStyle('display', 'block');
4655         this.el.removeClass('hideing');
4656         this.el.dom.style.display='block';
4657         
4658         Roo.get(document.body).addClass('modal-open');
4659  
4660         if(this.animate){  // element has 'fade'  - so stuff happens after .3s ?- not sure why the delay?
4661             
4662             (function(){
4663                 this.el.addClass('show');
4664                 this.el.addClass('in');
4665             }).defer(50, this);
4666         }else{
4667             this.el.addClass('show');
4668             this.el.addClass('in');
4669         }
4670
4671         // not sure how we can show data in here..
4672         //if (this.tmpl) {
4673         //    this.getChildContainer().dom.innerHTML = this.tmpl.applyTemplate(this);
4674         //}
4675
4676         Roo.get(document.body).addClass("x-body-masked");
4677         
4678         this.maskEl.setSize(Roo.lib.Dom.getViewWidth(true),   Roo.lib.Dom.getViewHeight(true));
4679         this.maskEl.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
4680         this.maskEl.dom.style.display = 'block';
4681         this.maskEl.addClass('show');
4682         
4683         
4684         this.resize();
4685         
4686         this.fireEvent('show', this);
4687
4688         // set zindex here - otherwise it appears to be ignored...
4689         this.el.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
4690         
4691         
4692         // this is for children that are... layout.Border 
4693         (function () {
4694             this.items.forEach( function(e) {
4695                 e.layout ? e.layout() : false;
4696
4697             });
4698         }).defer(100,this);
4699
4700     },
4701     hide : function()
4702     {
4703         if(this.fireEvent("beforehide", this) !== false){
4704             
4705             this.maskEl.removeClass('show');
4706             
4707             this.maskEl.dom.style.display = '';
4708             Roo.get(document.body).removeClass("x-body-masked");
4709             this.el.removeClass('in');
4710             this.el.select('.modal-dialog', true).first().setStyle('transform','');
4711
4712             if(this.animate){ // why
4713                 this.el.addClass('hideing');
4714                 this.el.removeClass('show');
4715                 (function(){
4716                     if (!this.el.hasClass('hideing')) {
4717                         return; // it's been shown again...
4718                     }
4719                     
4720                     this.el.dom.style.display='';
4721
4722                     Roo.get(document.body).removeClass('modal-open');
4723                     this.el.removeClass('hideing');
4724                 }).defer(150,this);
4725                 
4726             }else{
4727                 this.el.removeClass('show');
4728                 this.el.dom.style.display='';
4729                 Roo.get(document.body).removeClass('modal-open');
4730
4731             }
4732             this.fireEvent('hide', this);
4733         }
4734     },
4735     isVisible : function()
4736     {
4737         
4738         return this.el.hasClass('show') && !this.el.hasClass('hideing');
4739         
4740     },
4741
4742     addButton : function(str, cb)
4743     {
4744
4745
4746         var b = Roo.apply({}, { html : str } );
4747         b.xns = b.xns || Roo.bootstrap;
4748         b.xtype = b.xtype || 'Button';
4749         if (typeof(b.listeners) == 'undefined') {
4750             b.listeners = { click : cb.createDelegate(this)  };
4751         }
4752
4753         var btn = Roo.factory(b);
4754
4755         btn.render(this.getButtonContainer());
4756
4757         return btn;
4758
4759     },
4760
4761     setDefaultButton : function(btn)
4762     {
4763         //this.el.select('.modal-footer').()
4764     },
4765
4766     resizeTo: function(w,h)
4767     {
4768         this.dialogEl.setWidth(w);
4769         
4770         var diff = this.headerEl.getHeight() + this.footerEl.getHeight() + 60; // dialog margin-bottom: 30  
4771
4772         this.bodyEl.setHeight(h - diff);
4773         
4774         this.fireEvent('resize', this);
4775     },
4776     
4777     setContentSize  : function(w, h)
4778     {
4779
4780     },
4781     onButtonClick: function(btn,e)
4782     {
4783         //Roo.log([a,b,c]);
4784         this.fireEvent('btnclick', btn.name, e);
4785     },
4786      /**
4787      * Set the title of the Dialog
4788      * @param {String} str new Title
4789      */
4790     setTitle: function(str) {
4791         this.titleEl.dom.innerHTML = str;
4792         this.title = str;
4793     },
4794     /**
4795      * Set the body of the Dialog
4796      * @param {String} str new Title
4797      */
4798     setBody: function(str) {
4799         this.bodyEl.dom.innerHTML = str;
4800     },
4801     /**
4802      * Set the body of the Dialog using the template
4803      * @param {Obj} data - apply this data to the template and replace the body contents.
4804      */
4805     applyBody: function(obj)
4806     {
4807         if (!this.tmpl) {
4808             Roo.log("Error - using apply Body without a template");
4809             //code
4810         }
4811         this.tmpl.overwrite(this.bodyEl, obj);
4812     },
4813     
4814     getChildHeight : function(child_nodes)
4815     {
4816         if(
4817             !child_nodes ||
4818             child_nodes.length == 0
4819         ) {
4820             return 0;
4821         }
4822         
4823         var child_height = 0;
4824         
4825         for(var i = 0; i < child_nodes.length; i++) {
4826             
4827             /*
4828             * for modal with tabs...
4829             if(child_nodes[i].classList.contains('roo-layout-panel')) {
4830                 
4831                 var layout_childs = child_nodes[i].childNodes;
4832                 
4833                 for(var j = 0; j < layout_childs.length; j++) {
4834                     
4835                     if(layout_childs[j].classList.contains('roo-layout-panel-body')) {
4836                         
4837                         var layout_body_childs = layout_childs[j].childNodes;
4838                         
4839                         for(var k = 0; k < layout_body_childs.length; k++) {
4840                             
4841                             if(layout_body_childs[k].classList.contains('navbar')) {
4842                                 child_height += layout_body_childs[k].offsetHeight;
4843                                 continue;
4844                             }
4845                             
4846                             if(layout_body_childs[k].classList.contains('roo-layout-tabs-body')) {
4847                                 
4848                                 var layout_body_tab_childs = layout_body_childs[k].childNodes;
4849                                 
4850                                 for(var m = 0; m < layout_body_tab_childs.length; m++) {
4851                                     
4852                                     if(layout_body_tab_childs[m].classList.contains('roo-layout-active-content')) {
4853                                         child_height += this.getChildHeight(layout_body_tab_childs[m].childNodes);
4854                                         continue;
4855                                     }
4856                                     
4857                                 }
4858                                 
4859                             }
4860                             
4861                         }
4862                     }
4863                 }
4864                 continue;
4865             }
4866             */
4867             
4868             child_height += child_nodes[i].offsetHeight;
4869             // Roo.log(child_nodes[i].offsetHeight);
4870         }
4871         
4872         return child_height;
4873     },
4874     toggleHeaderInput : function(is_edit)
4875     {
4876         if (!this.editableTitle) {
4877             return; // not editable.
4878         }
4879         if (is_edit && this.is_header_editing) {
4880             return; // already editing..
4881         }
4882         if (is_edit) {
4883     
4884             this.headerEditEl.dom.value = this.title;
4885             this.headerEditEl.removeClass('d-none');
4886             this.headerEditEl.dom.focus();
4887             this.titleEl.addClass('d-none');
4888             
4889             this.is_header_editing = true;
4890             return
4891         }
4892         // flip back to not editing.
4893         this.title = this.headerEditEl.dom.value;
4894         this.headerEditEl.addClass('d-none');
4895         this.titleEl.removeClass('d-none');
4896         this.titleEl.dom.innerHTML = String.format('{0}', this.title);
4897         this.is_header_editing = false;
4898         this.fireEvent('titlechanged', this, this.title);
4899     
4900             
4901         
4902     }
4903
4904 });
4905
4906
4907 Roo.apply(Roo.bootstrap.Modal,  {
4908     /**
4909          * Button config that displays a single OK button
4910          * @type Object
4911          */
4912         OK :  [{
4913             name : 'ok',
4914             weight : 'primary',
4915             html : 'OK'
4916         }],
4917         /**
4918          * Button config that displays Yes and No buttons
4919          * @type Object
4920          */
4921         YESNO : [
4922             {
4923                 name  : 'no',
4924                 html : 'No'
4925             },
4926             {
4927                 name  :'yes',
4928                 weight : 'primary',
4929                 html : 'Yes'
4930             }
4931         ],
4932
4933         /**
4934          * Button config that displays OK and Cancel buttons
4935          * @type Object
4936          */
4937         OKCANCEL : [
4938             {
4939                name : 'cancel',
4940                 html : 'Cancel'
4941             },
4942             {
4943                 name : 'ok',
4944                 weight : 'primary',
4945                 html : 'OK'
4946             }
4947         ],
4948         /**
4949          * Button config that displays Yes, No and Cancel buttons
4950          * @type Object
4951          */
4952         YESNOCANCEL : [
4953             {
4954                 name : 'yes',
4955                 weight : 'primary',
4956                 html : 'Yes'
4957             },
4958             {
4959                 name : 'no',
4960                 html : 'No'
4961             },
4962             {
4963                 name : 'cancel',
4964                 html : 'Cancel'
4965             }
4966         ],
4967         
4968         zIndex : 10001
4969 });
4970
4971 /*
4972  * - LGPL
4973  *
4974  * messagebox - can be used as a replace
4975  * 
4976  */
4977 /**
4978  * @class Roo.MessageBox
4979  * Utility class for generating different styles of message boxes.  The alias Roo.Msg can also be used.
4980  * Example usage:
4981  *<pre><code>
4982 // Basic alert:
4983 Roo.Msg.alert('Status', 'Changes saved successfully.');
4984
4985 // Prompt for user data:
4986 Roo.Msg.prompt('Name', 'Please enter your name:', function(btn, text){
4987     if (btn == 'ok'){
4988         // process text value...
4989     }
4990 });
4991
4992 // Show a dialog using config options:
4993 Roo.Msg.show({
4994    title:'Save Changes?',
4995    msg: 'Your are closing a tab that has unsaved changes. Would you like to save your changes?',
4996    buttons: Roo.Msg.YESNOCANCEL,
4997    fn: processResult,
4998    animEl: 'elId'
4999 });
5000 </code></pre>
5001  * @static
5002  */
5003 Roo.bootstrap.MessageBox = function(){
5004     var dlg, opt, mask, waitTimer;
5005     var bodyEl, msgEl, textboxEl, textareaEl, progressEl, pp;
5006     var buttons, activeTextEl, bwidth;
5007
5008     
5009     // private
5010     var handleButton = function(button){
5011         dlg.hide();
5012         Roo.callback(opt.fn, opt.scope||window, [button, activeTextEl.dom.value], 1);
5013     };
5014
5015     // private
5016     var handleHide = function(){
5017         if(opt && opt.cls){
5018             dlg.el.removeClass(opt.cls);
5019         }
5020         //if(waitTimer){
5021         //    Roo.TaskMgr.stop(waitTimer);
5022         //    waitTimer = null;
5023         //}
5024     };
5025
5026     // private
5027     var updateButtons = function(b){
5028         var width = 0;
5029         if(!b){
5030             buttons["ok"].hide();
5031             buttons["cancel"].hide();
5032             buttons["yes"].hide();
5033             buttons["no"].hide();
5034             dlg.footerEl.hide();
5035             
5036             return width;
5037         }
5038         dlg.footerEl.show();
5039         for(var k in buttons){
5040             if(typeof buttons[k] != "function"){
5041                 if(b[k]){
5042                     buttons[k].show();
5043                     buttons[k].setText(typeof b[k] == "string" ? b[k] : Roo.bootstrap.MessageBox.buttonText[k]);
5044                     width += buttons[k].el.getWidth()+15;
5045                 }else{
5046                     buttons[k].hide();
5047                 }
5048             }
5049         }
5050         return width;
5051     };
5052
5053     // private
5054     var handleEsc = function(d, k, e){
5055         if(opt && opt.closable !== false){
5056             dlg.hide();
5057         }
5058         if(e){
5059             e.stopEvent();
5060         }
5061     };
5062
5063     return {
5064         /**
5065          * Returns a reference to the underlying {@link Roo.BasicDialog} element
5066          * @return {Roo.BasicDialog} The BasicDialog element
5067          */
5068         getDialog : function(){
5069            if(!dlg){
5070                 dlg = new Roo.bootstrap.Modal( {
5071                     //draggable: true,
5072                     //resizable:false,
5073                     //constraintoviewport:false,
5074                     //fixedcenter:true,
5075                     //collapsible : false,
5076                     //shim:true,
5077                     //modal: true,
5078                 //    width: 'auto',
5079                   //  height:100,
5080                     //buttonAlign:"center",
5081                     closeClick : function(){
5082                         if(opt && opt.buttons && opt.buttons.no && !opt.buttons.cancel){
5083                             handleButton("no");
5084                         }else{
5085                             handleButton("cancel");
5086                         }
5087                     }
5088                 });
5089                 dlg.render();
5090                 dlg.on("hide", handleHide);
5091                 mask = dlg.mask;
5092                 //dlg.addKeyListener(27, handleEsc);
5093                 buttons = {};
5094                 this.buttons = buttons;
5095                 var bt = this.buttonText;
5096                 buttons["ok"] = dlg.addButton(bt["ok"], handleButton.createCallback("ok"));
5097                 buttons["yes"] = dlg.addButton(bt["yes"], handleButton.createCallback("yes"));
5098                 buttons["no"] = dlg.addButton(bt["no"], handleButton.createCallback("no"));
5099                 buttons["cancel"] = dlg.addButton(bt["cancel"], handleButton.createCallback("cancel"));
5100                 //Roo.log(buttons);
5101                 bodyEl = dlg.bodyEl.createChild({
5102
5103                     html:'<span class="roo-mb-text"></span><br /><input type="text" class="roo-mb-input" />' +
5104                         '<textarea class="roo-mb-textarea"></textarea>' +
5105                         '<div class="roo-mb-progress-wrap"><div class="roo-mb-progress"><div class="roo-mb-progress-bar">&#160;</div></div></div>'
5106                 });
5107                 msgEl = bodyEl.dom.firstChild;
5108                 textboxEl = Roo.get(bodyEl.dom.childNodes[2]);
5109                 textboxEl.enableDisplayMode();
5110                 textboxEl.addKeyListener([10,13], function(){
5111                     if(dlg.isVisible() && opt && opt.buttons){
5112                         if(opt.buttons.ok){
5113                             handleButton("ok");
5114                         }else if(opt.buttons.yes){
5115                             handleButton("yes");
5116                         }
5117                     }
5118                 });
5119                 textareaEl = Roo.get(bodyEl.dom.childNodes[3]);
5120                 textareaEl.enableDisplayMode();
5121                 progressEl = Roo.get(bodyEl.dom.childNodes[4]);
5122                 progressEl.enableDisplayMode();
5123                 
5124                 // This is supposed to be the progessElement.. but I think it's controlling the height of everything..
5125                 var pf = progressEl.dom.firstChild;
5126                 if (pf) {
5127                     pp = Roo.get(pf.firstChild);
5128                     pp.setHeight(pf.offsetHeight);
5129                 }
5130                 
5131             }
5132             return dlg;
5133         },
5134
5135         /**
5136          * Updates the message box body text
5137          * @param {String} text (optional) Replaces the message box element's innerHTML with the specified string (defaults to
5138          * the XHTML-compliant non-breaking space character '&amp;#160;')
5139          * @return {Roo.MessageBox} This message box
5140          */
5141         updateText : function(text)
5142         {
5143             if(!dlg.isVisible() && !opt.width){
5144                 dlg.dialogEl.setStyle({ 'max-width' : this.maxWidth});
5145                 // dlg.resizeTo(this.maxWidth, 100); // forcing the height breaks long alerts()
5146             }
5147             msgEl.innerHTML = text || '&#160;';
5148       
5149             var cw =  Math.max(msgEl.offsetWidth, msgEl.parentNode.scrollWidth);
5150             //Roo.log("guesed size: " + JSON.stringify([cw,msgEl.offsetWidth, msgEl.parentNode.scrollWidth]));
5151             var w = Math.max(
5152                     Math.min(opt.width || cw , this.maxWidth), 
5153                     Math.max(opt.minWidth || this.minWidth, bwidth)
5154             );
5155             if(opt.prompt){
5156                 activeTextEl.setWidth(w);
5157             }
5158             if(dlg.isVisible()){
5159                 dlg.fixedcenter = false;
5160             }
5161             // to big, make it scroll. = But as usual stupid IE does not support
5162             // !important..
5163             
5164             if ( bodyEl.getHeight() > (Roo.lib.Dom.getViewHeight() - 100)) {
5165                 bodyEl.setHeight ( Roo.lib.Dom.getViewHeight() - 100 );
5166                 bodyEl.dom.style.overflowY = 'auto' + ( Roo.isIE ? '' : ' !important');
5167             } else {
5168                 bodyEl.dom.style.height = '';
5169                 bodyEl.dom.style.overflowY = '';
5170             }
5171             if (cw > w) {
5172                 bodyEl.dom.style.get = 'auto' + ( Roo.isIE ? '' : ' !important');
5173             } else {
5174                 bodyEl.dom.style.overflowX = '';
5175             }
5176             
5177             dlg.setContentSize(w, bodyEl.getHeight());
5178             if(dlg.isVisible()){
5179                 dlg.fixedcenter = true;
5180             }
5181             return this;
5182         },
5183
5184         /**
5185          * Updates a progress-style message box's text and progress bar.  Only relevant on message boxes
5186          * initiated via {@link Roo.MessageBox#progress} or by calling {@link Roo.MessageBox#show} with progress: true.
5187          * @param {Number} value Any number between 0 and 1 (e.g., .5)
5188          * @param {String} text (optional) If defined, the message box's body text is replaced with the specified string (defaults to undefined)
5189          * @return {Roo.MessageBox} This message box
5190          */
5191         updateProgress : function(value, text){
5192             if(text){
5193                 this.updateText(text);
5194             }
5195             
5196             if (pp) { // weird bug on my firefox - for some reason this is not defined
5197                 pp.setWidth(Math.floor(value*progressEl.dom.firstChild.offsetWidth));
5198                 pp.setHeight(Math.floor(progressEl.dom.firstChild.offsetHeight));
5199             }
5200             return this;
5201         },        
5202
5203         /**
5204          * Returns true if the message box is currently displayed
5205          * @return {Boolean} True if the message box is visible, else false
5206          */
5207         isVisible : function(){
5208             return dlg && dlg.isVisible();  
5209         },
5210
5211         /**
5212          * Hides the message box if it is displayed
5213          */
5214         hide : function(){
5215             if(this.isVisible()){
5216                 dlg.hide();
5217             }  
5218         },
5219
5220         /**
5221          * Displays a new message box, or reinitializes an existing message box, based on the config options
5222          * passed in. All functions (e.g. prompt, alert, etc) on MessageBox call this function internally.
5223          * The following config object properties are supported:
5224          * <pre>
5225 Property    Type             Description
5226 ----------  ---------------  ------------------------------------------------------------------------------------
5227 animEl            String/Element   An id or Element from which the message box should animate as it opens and
5228                                    closes (defaults to undefined)
5229 buttons           Object/Boolean   A button config object (e.g., Roo.MessageBox.OKCANCEL or {ok:'Foo',
5230                                    cancel:'Bar'}), or false to not show any buttons (defaults to false)
5231 closable          Boolean          False to hide the top-right close button (defaults to true).  Note that
5232                                    progress and wait dialogs will ignore this property and always hide the
5233                                    close button as they can only be closed programmatically.
5234 cls               String           A custom CSS class to apply to the message box element
5235 defaultTextHeight Number           The default height in pixels of the message box's multiline textarea if
5236                                    displayed (defaults to 75)
5237 fn                Function         A callback function to execute after closing the dialog.  The arguments to the
5238                                    function will be btn (the name of the button that was clicked, if applicable,
5239                                    e.g. "ok"), and text (the value of the active text field, if applicable).
5240                                    Progress and wait dialogs will ignore this option since they do not respond to
5241                                    user actions and can only be closed programmatically, so any required function
5242                                    should be called by the same code after it closes the dialog.
5243 icon              String           A CSS class that provides a background image to be used as an icon for
5244                                    the dialog (e.g., Roo.MessageBox.WARNING or 'custom-class', defaults to '')
5245 maxWidth          Number           The maximum width in pixels of the message box (defaults to 600)
5246 minWidth          Number           The minimum width in pixels of the message box (defaults to 100)
5247 modal             Boolean          False to allow user interaction with the page while the message box is
5248                                    displayed (defaults to true)
5249 msg               String           A string that will replace the existing message box body text (defaults
5250                                    to the XHTML-compliant non-breaking space character '&#160;')
5251 multiline         Boolean          True to prompt the user to enter multi-line text (defaults to false)
5252 progress          Boolean          True to display a progress bar (defaults to false)
5253 progressText      String           The text to display inside the progress bar if progress = true (defaults to '')
5254 prompt            Boolean          True to prompt the user to enter single-line text (defaults to false)
5255 proxyDrag         Boolean          True to display a lightweight proxy while dragging (defaults to false)
5256 title             String           The title text
5257 value             String           The string value to set into the active textbox element if displayed
5258 wait              Boolean          True to display a progress bar (defaults to false)
5259 width             Number           The width of the dialog in pixels
5260 </pre>
5261          *
5262          * Example usage:
5263          * <pre><code>
5264 Roo.Msg.show({
5265    title: 'Address',
5266    msg: 'Please enter your address:',
5267    width: 300,
5268    buttons: Roo.MessageBox.OKCANCEL,
5269    multiline: true,
5270    fn: saveAddress,
5271    animEl: 'addAddressBtn'
5272 });
5273 </code></pre>
5274          * @param {Object} config Configuration options
5275          * @return {Roo.MessageBox} This message box
5276          */
5277         show : function(options)
5278         {
5279             
5280             // this causes nightmares if you show one dialog after another
5281             // especially on callbacks..
5282              
5283             if(this.isVisible()){
5284                 
5285                 this.hide();
5286                 Roo.log("[Roo.Messagebox] Show called while message displayed:" );
5287                 Roo.log("Old Dialog Message:" +  msgEl.innerHTML );
5288                 Roo.log("New Dialog Message:" +  options.msg )
5289                 //this.alert("ERROR", "Multiple dialogs where displayed at the same time");
5290                 //throw "Roo.MessageBox ERROR : Multiple dialogs where displayed at the same time";
5291                 
5292             }
5293             var d = this.getDialog();
5294             opt = options;
5295             d.setTitle(opt.title || "&#160;");
5296             d.closeEl.setDisplayed(opt.closable !== false);
5297             activeTextEl = textboxEl;
5298             opt.prompt = opt.prompt || (opt.multiline ? true : false);
5299             if(opt.prompt){
5300                 if(opt.multiline){
5301                     textboxEl.hide();
5302                     textareaEl.show();
5303                     textareaEl.setHeight(typeof opt.multiline == "number" ?
5304                         opt.multiline : this.defaultTextHeight);
5305                     activeTextEl = textareaEl;
5306                 }else{
5307                     textboxEl.show();
5308                     textareaEl.hide();
5309                 }
5310             }else{
5311                 textboxEl.hide();
5312                 textareaEl.hide();
5313             }
5314             progressEl.setDisplayed(opt.progress === true);
5315             if (opt.progress) {
5316                 d.animate = false; // do not animate progress, as it may not have finished animating before we close it..
5317             }
5318             this.updateProgress(0);
5319             activeTextEl.dom.value = opt.value || "";
5320             if(opt.prompt){
5321                 dlg.setDefaultButton(activeTextEl);
5322             }else{
5323                 var bs = opt.buttons;
5324                 var db = null;
5325                 if(bs && bs.ok){
5326                     db = buttons["ok"];
5327                 }else if(bs && bs.yes){
5328                     db = buttons["yes"];
5329                 }
5330                 dlg.setDefaultButton(db);
5331             }
5332             bwidth = updateButtons(opt.buttons);
5333             this.updateText(opt.msg);
5334             if(opt.cls){
5335                 d.el.addClass(opt.cls);
5336             }
5337             d.proxyDrag = opt.proxyDrag === true;
5338             d.modal = opt.modal !== false;
5339             d.mask = opt.modal !== false ? mask : false;
5340             if(!d.isVisible()){
5341                 // force it to the end of the z-index stack so it gets a cursor in FF
5342                 document.body.appendChild(dlg.el.dom);
5343                 d.animateTarget = null;
5344                 d.show(options.animEl);
5345             }
5346             return this;
5347         },
5348
5349         /**
5350          * Displays a message box with a progress bar.  This message box has no buttons and is not closeable by
5351          * the user.  You are responsible for updating the progress bar as needed via {@link Roo.MessageBox#updateProgress}
5352          * and closing the message box when the process is complete.
5353          * @param {String} title The title bar text
5354          * @param {String} msg The message box body text
5355          * @return {Roo.MessageBox} This message box
5356          */
5357         progress : function(title, msg){
5358             this.show({
5359                 title : title,
5360                 msg : msg,
5361                 buttons: false,
5362                 progress:true,
5363                 closable:false,
5364                 minWidth: this.minProgressWidth,
5365                 modal : true
5366             });
5367             return this;
5368         },
5369
5370         /**
5371          * Displays a standard read-only message box with an OK button (comparable to the basic JavaScript Window.alert).
5372          * If a callback function is passed it will be called after the user clicks the button, and the
5373          * id of the button that was clicked will be passed as the only parameter to the callback
5374          * (could also be the top-right close button).
5375          * @param {String} title The title bar text
5376          * @param {String} msg The message box body text
5377          * @param {Function} fn (optional) The callback function invoked after the message box is closed
5378          * @param {Object} scope (optional) The scope of the callback function
5379          * @return {Roo.MessageBox} This message box
5380          */
5381         alert : function(title, msg, fn, scope)
5382         {
5383             this.show({
5384                 title : title,
5385                 msg : msg,
5386                 buttons: this.OK,
5387                 fn: fn,
5388                 closable : false,
5389                 scope : scope,
5390                 modal : true
5391             });
5392             return this;
5393         },
5394
5395         /**
5396          * Displays a message box with an infinitely auto-updating progress bar.  This can be used to block user
5397          * interaction while waiting for a long-running process to complete that does not have defined intervals.
5398          * You are responsible for closing the message box when the process is complete.
5399          * @param {String} msg The message box body text
5400          * @param {String} title (optional) The title bar text
5401          * @return {Roo.MessageBox} This message box
5402          */
5403         wait : function(msg, title){
5404             this.show({
5405                 title : title,
5406                 msg : msg,
5407                 buttons: false,
5408                 closable:false,
5409                 progress:true,
5410                 modal:true,
5411                 width:300,
5412                 wait:true
5413             });
5414             waitTimer = Roo.TaskMgr.start({
5415                 run: function(i){
5416                     Roo.MessageBox.updateProgress(((((i+20)%20)+1)*5)*.01);
5417                 },
5418                 interval: 1000
5419             });
5420             return this;
5421         },
5422
5423         /**
5424          * Displays a confirmation message box with Yes and No buttons (comparable to JavaScript's Window.confirm).
5425          * If a callback function is passed it will be called after the user clicks either button, and the id of the
5426          * button that was clicked will be passed as the only parameter to the callback (could also be the top-right close button).
5427          * @param {String} title The title bar text
5428          * @param {String} msg The message box body text
5429          * @param {Function} fn (optional) The callback function invoked after the message box is closed
5430          * @param {Object} scope (optional) The scope of the callback function
5431          * @return {Roo.MessageBox} This message box
5432          */
5433         confirm : function(title, msg, fn, scope){
5434             this.show({
5435                 title : title,
5436                 msg : msg,
5437                 buttons: this.YESNO,
5438                 fn: fn,
5439                 scope : scope,
5440                 modal : true
5441             });
5442             return this;
5443         },
5444
5445         /**
5446          * Displays a message box with OK and Cancel buttons prompting the user to enter some text (comparable to
5447          * JavaScript's Window.prompt).  The prompt can be a single-line or multi-line textbox.  If a callback function
5448          * is passed it will be called after the user clicks either button, and the id of the button that was clicked
5449          * (could also be the top-right close button) and the text that was entered will be passed as the two
5450          * parameters to the callback.
5451          * @param {String} title The title bar text
5452          * @param {String} msg The message box body text
5453          * @param {Function} fn (optional) The callback function invoked after the message box is closed
5454          * @param {Object} scope (optional) The scope of the callback function
5455          * @param {Boolean/Number} multiline (optional) True to create a multiline textbox using the defaultTextHeight
5456          * property, or the height in pixels to create the textbox (defaults to false / single-line)
5457          * @return {Roo.MessageBox} This message box
5458          */
5459         prompt : function(title, msg, fn, scope, multiline){
5460             this.show({
5461                 title : title,
5462                 msg : msg,
5463                 buttons: this.OKCANCEL,
5464                 fn: fn,
5465                 minWidth:250,
5466                 scope : scope,
5467                 prompt:true,
5468                 multiline: multiline,
5469                 modal : true
5470             });
5471             return this;
5472         },
5473
5474         /**
5475          * Button config that displays a single OK button
5476          * @type Object
5477          */
5478         OK : {ok:true},
5479         /**
5480          * Button config that displays Yes and No buttons
5481          * @type Object
5482          */
5483         YESNO : {yes:true, no:true},
5484         /**
5485          * Button config that displays OK and Cancel buttons
5486          * @type Object
5487          */
5488         OKCANCEL : {ok:true, cancel:true},
5489         /**
5490          * Button config that displays Yes, No and Cancel buttons
5491          * @type Object
5492          */
5493         YESNOCANCEL : {yes:true, no:true, cancel:true},
5494
5495         /**
5496          * The default height in pixels of the message box's multiline textarea if displayed (defaults to 75)
5497          * @type Number
5498          */
5499         defaultTextHeight : 75,
5500         /**
5501          * The maximum width in pixels of the message box (defaults to 600)
5502          * @type Number
5503          */
5504         maxWidth : 600,
5505         /**
5506          * The minimum width in pixels of the message box (defaults to 100)
5507          * @type Number
5508          */
5509         minWidth : 100,
5510         /**
5511          * The minimum width in pixels of the message box if it is a progress-style dialog.  This is useful
5512          * for setting a different minimum width than text-only dialogs may need (defaults to 250)
5513          * @type Number
5514          */
5515         minProgressWidth : 250,
5516         /**
5517          * An object containing the default button text strings that can be overriden for localized language support.
5518          * Supported properties are: ok, cancel, yes and no.
5519          * Customize the default text like so: Roo.MessageBox.buttonText.yes = "S?";
5520          * @type Object
5521          */
5522         buttonText : {
5523             ok : "OK",
5524             cancel : "Cancel",
5525             yes : "Yes",
5526             no : "No"
5527         }
5528     };
5529 }();
5530
5531 /**
5532  * Shorthand for {@link Roo.MessageBox}
5533  */
5534 Roo.MessageBox = Roo.MessageBox || Roo.bootstrap.MessageBox;
5535 Roo.Msg = Roo.Msg || Roo.MessageBox;
5536 /*
5537  * - LGPL
5538  *
5539  * navbar
5540  * 
5541  */
5542
5543 /**
5544  * @class Roo.bootstrap.nav.Bar
5545  * @extends Roo.bootstrap.Component
5546  * @abstract
5547  * Bootstrap Navbar class
5548
5549  * @constructor
5550  * Create a new Navbar
5551  * @param {Object} config The config object
5552  */
5553
5554
5555 Roo.bootstrap.nav.Bar = function(config){
5556     Roo.bootstrap.nav.Bar.superclass.constructor.call(this, config);
5557     this.addEvents({
5558         // raw events
5559         /**
5560          * @event beforetoggle
5561          * Fire before toggle the menu
5562          * @param {Roo.EventObject} e
5563          */
5564         "beforetoggle" : true
5565     });
5566 };
5567
5568 Roo.extend(Roo.bootstrap.nav.Bar, Roo.bootstrap.Component,  {
5569     
5570     
5571    
5572     // private
5573     navItems : false,
5574     loadMask : false,
5575     
5576     
5577     getAutoCreate : function(){
5578         
5579         
5580         throw { message : "nav bar is now a abstract base class - use NavSimplebar / NavHeaderbar / NavSidebar etc..."};
5581         
5582     },
5583     
5584     initEvents :function ()
5585     {
5586         //Roo.log(this.el.select('.navbar-toggle',true));
5587         this.el.select('.navbar-toggle',true).on('click', this.onToggle , this);
5588         
5589         var mark = {
5590             tag: "div",
5591             cls:"x-dlg-mask"
5592         };
5593         
5594         this.maskEl = Roo.DomHelper.append(this.el, mark, true);
5595         
5596         var size = this.el.getSize();
5597         this.maskEl.setSize(size.width, size.height);
5598         this.maskEl.enableDisplayMode("block");
5599         this.maskEl.hide();
5600         
5601         if(this.loadMask){
5602             this.maskEl.show();
5603         }
5604     },
5605     
5606     
5607     getChildContainer : function()
5608     {
5609         if (this.el && this.el.select('.collapse').getCount()) {
5610             return this.el.select('.collapse',true).first();
5611         }
5612         
5613         return this.el;
5614     },
5615     
5616     mask : function()
5617     {
5618         this.maskEl.show();
5619     },
5620     
5621     unmask : function()
5622     {
5623         this.maskEl.hide();
5624     },
5625     onToggle : function()
5626     {
5627         
5628         if(this.fireEvent('beforetoggle', this) === false){
5629             return;
5630         }
5631         var ce = this.el.select('.navbar-collapse',true).first();
5632       
5633         if (!ce.hasClass('show')) {
5634            this.expand();
5635         } else {
5636             this.collapse();
5637         }
5638         
5639         
5640     
5641     },
5642     /**
5643      * Expand the navbar pulldown 
5644      */
5645     expand : function ()
5646     {
5647        
5648         var ce = this.el.select('.navbar-collapse',true).first();
5649         if (ce.hasClass('collapsing')) {
5650             return;
5651         }
5652         ce.dom.style.height = '';
5653                // show it...
5654         ce.addClass('in'); // old...
5655         ce.removeClass('collapse');
5656         ce.addClass('show');
5657         var h = ce.getHeight();
5658         Roo.log(h);
5659         ce.removeClass('show');
5660         // at this point we should be able to see it..
5661         ce.addClass('collapsing');
5662         
5663         ce.setHeight(0); // resize it ...
5664         ce.on('transitionend', function() {
5665             //Roo.log('done transition');
5666             ce.removeClass('collapsing');
5667             ce.addClass('show');
5668             ce.removeClass('collapse');
5669
5670             ce.dom.style.height = '';
5671         }, this, { single: true} );
5672         ce.setHeight(h);
5673         ce.dom.scrollTop = 0;
5674     },
5675     /**
5676      * Collapse the navbar pulldown 
5677      */
5678     collapse : function()
5679     {
5680          var ce = this.el.select('.navbar-collapse',true).first();
5681        
5682         if (ce.hasClass('collapsing') || ce.hasClass('collapse') ) {
5683             // it's collapsed or collapsing..
5684             return;
5685         }
5686         ce.removeClass('in'); // old...
5687         ce.setHeight(ce.getHeight());
5688         ce.removeClass('show');
5689         ce.addClass('collapsing');
5690         
5691         ce.on('transitionend', function() {
5692             ce.dom.style.height = '';
5693             ce.removeClass('collapsing');
5694             ce.addClass('collapse');
5695         }, this, { single: true} );
5696         ce.setHeight(0);
5697     }
5698     
5699     
5700     
5701 });
5702
5703
5704
5705  
5706
5707  /*
5708  * - LGPL
5709  *
5710  * navbar
5711  * 
5712  */
5713
5714 /**
5715  * @class Roo.bootstrap.nav.Simplebar
5716  * @extends Roo.bootstrap.nav.Bar
5717  * @children Roo.bootstrap.nav.Group Roo.bootstrap.Container Roo.bootstrap.form.Form Roo.bootstrap.Row Roo.bootstrap.Column Roo.bootstrap.Link
5718  * Bootstrap Sidebar class
5719  *
5720  * @cfg {Boolean} inverse is inverted color
5721  * 
5722  * @cfg {String} type (nav | pills | tabs)
5723  * @cfg {Boolean} arrangement stacked | justified
5724  * @cfg {String} align (left | right) alignment
5725  * 
5726  * @cfg {Boolean} main (true|false) main nav bar? default false
5727  * @cfg {Boolean} loadMask (true|false) loadMask on the bar
5728  * 
5729  * @cfg {String} tag (header|footer|nav|div) default is nav 
5730
5731  * @cfg {String} weight (light|primary|secondary|success|danger|warning|info|dark|white) default is light.
5732  * 
5733  * 
5734  * @constructor
5735  * Create a new Sidebar
5736  * @param {Object} config The config object
5737  */
5738
5739
5740 Roo.bootstrap.nav.Simplebar = function(config){
5741     Roo.bootstrap.nav.Simplebar.superclass.constructor.call(this, config);
5742 };
5743
5744 Roo.extend(Roo.bootstrap.nav.Simplebar, Roo.bootstrap.nav.Bar,  {
5745     
5746     inverse: false,
5747     
5748     type: false,
5749     arrangement: '',
5750     align : false,
5751     
5752     weight : 'light',
5753     
5754     main : false,
5755     
5756     
5757     tag : false,
5758     
5759     
5760     getAutoCreate : function(){
5761         
5762         
5763         var cfg = {
5764             tag : this.tag || 'div',
5765             cls : 'navbar roo-navbar-simple' //navbar-expand-lg ??
5766         };
5767         if (['light','white'].indexOf(this.weight) > -1) {
5768             cfg.cls += ['light','white'].indexOf(this.weight) > -1 ? ' navbar-light' : ' navbar-dark';
5769         }
5770         cfg.cls += ' bg-' + this.weight;
5771         
5772         if (this.inverse) {
5773             cfg.cls += ' navbar-inverse';
5774             
5775         }
5776         
5777         // i'm not actually sure these are really used - normally we add a navGroup to a navbar
5778         
5779         if (Roo.bootstrap.version == 4 && this.xtype == 'NavSimplebar') {
5780             return cfg;
5781         }
5782         
5783         
5784     
5785         
5786         cfg.cn = [
5787             {
5788                 cls: 'nav nav-' + this.xtype,
5789                 tag : 'ul'
5790             }
5791         ];
5792         
5793          
5794         this.type = this.type || 'nav';
5795         if (['tabs','pills'].indexOf(this.type) != -1) {
5796             cfg.cn[0].cls += ' nav-' + this.type
5797         
5798         
5799         } else {
5800             if (this.type!=='nav') {
5801                 Roo.log('nav type must be nav/tabs/pills')
5802             }
5803             cfg.cn[0].cls += ' navbar-nav'
5804         }
5805         
5806         
5807         
5808         
5809         if (['stacked','justified'].indexOf(this.arrangement) != -1) {
5810             cfg.cn[0].cls += ' nav-' + this.arrangement;
5811         }
5812         
5813         
5814         if (this.align === 'right') {
5815             cfg.cn[0].cls += ' navbar-right';
5816         }
5817         
5818         
5819         
5820         
5821         return cfg;
5822     
5823         
5824     }
5825     
5826     
5827     
5828 });
5829
5830
5831
5832  
5833
5834  
5835        /*
5836  * - LGPL
5837  *
5838  * navbar
5839  * navbar-fixed-top
5840  * navbar-expand-md  fixed-top 
5841  */
5842
5843 /**
5844  * @class Roo.bootstrap.nav.Headerbar
5845  * @extends Roo.bootstrap.nav.Simplebar
5846  * @children Roo.bootstrap.nav.Group Roo.bootstrap.Container Roo.bootstrap.form.Form Roo.bootstrap.Row Roo.bootstrap.Column Roo.bootstrap.Link
5847  * Bootstrap Sidebar class
5848  *
5849  * @cfg {String} brand what is brand
5850  * @cfg {String} position (fixed-top|fixed-bottom|static-top) position
5851  * @cfg {String} brand_href href of the brand
5852  * @cfg {Boolean} srButton generate the (screen reader / mobile) sr-only button   default true
5853  * @cfg {Boolean} autohide a top nav bar header that hides on scroll.
5854  * @cfg {Boolean} desktopCenter should the header be centered on desktop using a container class
5855  * @cfg {Roo.bootstrap.Row} mobilerow - a row to display on mobile only..
5856  * 
5857  * @constructor
5858  * Create a new Sidebar
5859  * @param {Object} config The config object
5860  */
5861
5862
5863 Roo.bootstrap.nav.Headerbar = function(config){
5864     Roo.bootstrap.nav.Headerbar.superclass.constructor.call(this, config);
5865       
5866 };
5867
5868 Roo.extend(Roo.bootstrap.nav.Headerbar, Roo.bootstrap.nav.Simplebar,  {
5869     
5870     position: '',
5871     brand: '',
5872     brand_href: false,
5873     srButton : true,
5874     autohide : false,
5875     desktopCenter : false,
5876    
5877     
5878     getAutoCreate : function(){
5879         
5880         var   cfg = {
5881             tag: this.nav || 'nav',
5882             cls: 'navbar navbar-expand-md',
5883             role: 'navigation',
5884             cn: []
5885         };
5886         
5887         var cn = cfg.cn;
5888         if (this.desktopCenter) {
5889             cn.push({cls : 'container', cn : []});
5890             cn = cn[0].cn;
5891         }
5892         
5893         if(this.srButton){
5894             var btn = {
5895                 tag: 'button',
5896                 type: 'button',
5897                 cls: 'navbar-toggle navbar-toggler',
5898                 'data-toggle': 'collapse',
5899                 cn: [
5900                     {
5901                         tag: 'span',
5902                         cls: 'sr-only',
5903                         html: 'Toggle navigation'
5904                     },
5905                     {
5906                         tag: 'span',
5907                         cls: 'icon-bar navbar-toggler-icon'
5908                     },
5909                     {
5910                         tag: 'span',
5911                         cls: 'icon-bar'
5912                     },
5913                     {
5914                         tag: 'span',
5915                         cls: 'icon-bar'
5916                     }
5917                 ]
5918             };
5919             
5920             cn.push( Roo.bootstrap.version == 4 ? btn : {
5921                 tag: 'div',
5922                 cls: 'navbar-header',
5923                 cn: [
5924                     btn
5925                 ]
5926             });
5927         }
5928         
5929         cn.push({
5930             tag: 'div',
5931             cls: Roo.bootstrap.version == 4  ? 'nav flex-row roo-navbar-collapse collapse navbar-collapse' : 'collapse navbar-collapse roo-navbar-collapse',
5932             cn : []
5933         });
5934         
5935         cfg.cls += this.inverse ? ' navbar-inverse navbar-dark bg-dark' : ' navbar-default';
5936         
5937         if (['light','white'].indexOf(this.weight) > -1) {
5938             cfg.cls += ['light','white'].indexOf(this.weight) > -1 ? ' navbar-light' : ' navbar-dark';
5939         }
5940         cfg.cls += ' bg-' + this.weight;
5941         
5942         
5943         if (['fixed-top','fixed-bottom','static-top'].indexOf(this.position)>-1) {
5944             cfg.cls += ' navbar-' + this.position + ' ' + this.position ;
5945             
5946             // tag can override this..
5947             
5948             cfg.tag = this.tag || (this.position  == 'fixed-bottom' ? 'footer' : 'header');
5949         }
5950         
5951         if (this.brand !== '') {
5952             var cp =  Roo.bootstrap.version == 4 ? cn : cn[0].cn;
5953             cp.unshift({ // changed from push ?? BS4 needs it at the start? - does this break or exsiting?
5954                 tag: 'a',
5955                 href: this.brand_href ? this.brand_href : '#',
5956                 cls: 'navbar-brand',
5957                 cn: [
5958                 this.brand
5959                 ]
5960             });
5961         }
5962         
5963         if(this.main){
5964             cfg.cls += ' main-nav';
5965         }
5966         
5967         
5968         return cfg;
5969
5970         
5971     },
5972     getHeaderChildContainer : function()
5973     {
5974         if (this.srButton && this.el.select('.navbar-header').getCount()) {
5975             return this.el.select('.navbar-header',true).first();
5976         }
5977         
5978         return this.getChildContainer();
5979     },
5980     
5981     getChildContainer : function()
5982     {
5983          
5984         return this.el.select('.roo-navbar-collapse',true).first();
5985          
5986         
5987     },
5988     
5989     initEvents : function()
5990     {
5991         Roo.bootstrap.nav.Headerbar.superclass.initEvents.call(this);
5992         
5993         if (this.autohide) {
5994             
5995             var prevScroll = 0;
5996             var ft = this.el;
5997             
5998             Roo.get(document).on('scroll',function(e) {
5999                 var ns = Roo.get(document).getScroll().top;
6000                 var os = prevScroll;
6001                 prevScroll = ns;
6002                 
6003                 if(ns > os){
6004                     ft.removeClass('slideDown');
6005                     ft.addClass('slideUp');
6006                     return;
6007                 }
6008                 ft.removeClass('slideUp');
6009                 ft.addClass('slideDown');
6010                  
6011               
6012           },this);
6013         }
6014     }    
6015     
6016 });
6017
6018
6019
6020  
6021
6022  /*
6023  * - LGPL
6024  *
6025  * navbar
6026  * 
6027  */
6028
6029 /**
6030  * @class Roo.bootstrap.nav.Sidebar
6031  * @extends Roo.bootstrap.nav.Bar
6032  * @children Roo.bootstrap.nav.Group Roo.bootstrap.Container Roo.bootstrap.form.Form Roo.bootstrap.Row Roo.bootstrap.Column Roo.bootstrap.Link
6033  * Bootstrap Sidebar class
6034  * 
6035  * @constructor
6036  * Create a new Sidebar
6037  * @param {Object} config The config object
6038  */
6039
6040
6041 Roo.bootstrap.nav.Sidebar = function(config){
6042     Roo.bootstrap.nav.Sidebar.superclass.constructor.call(this, config);
6043 };
6044
6045 Roo.extend(Roo.bootstrap.nav.Sidebar, Roo.bootstrap.nav.Bar,  {
6046     
6047     sidebar : true, // used by Navbar Item and NavbarGroup at present...
6048     
6049     getAutoCreate : function(){
6050         
6051         
6052         return  {
6053             tag: 'div',
6054             cls: 'sidebar sidebar-nav'
6055         };
6056     
6057         
6058     }
6059     
6060     
6061     
6062 });
6063
6064
6065
6066  
6067
6068  /*
6069  * - LGPL
6070  *
6071  * nav group
6072  * 
6073  */
6074
6075 /**
6076  * @class Roo.bootstrap.nav.Group
6077  * @extends Roo.bootstrap.Component
6078  * @children Roo.bootstrap.nav.Item
6079  * Bootstrap NavGroup class
6080  * @cfg {String} align (left|right)
6081  * @cfg {Boolean} inverse
6082  * @cfg {String} type (nav|pills|tab) default nav
6083  * @cfg {String} navId - reference Id for navbar.
6084  * @cfg {Boolean} pilltype default true (turn to off to disable active toggle)
6085  * 
6086  * @constructor
6087  * Create a new nav group
6088  * @param {Object} config The config object
6089  */
6090
6091 Roo.bootstrap.nav.Group = function(config){
6092     Roo.bootstrap.nav.Group.superclass.constructor.call(this, config);
6093     this.navItems = [];
6094    
6095     Roo.bootstrap.nav.Group.register(this);
6096      this.addEvents({
6097         /**
6098              * @event changed
6099              * Fires when the active item changes
6100              * @param {Roo.bootstrap.nav.Group} this
6101              * @param {Roo.bootstrap.Navbar.Item} selected The item selected
6102              * @param {Roo.bootstrap.Navbar.Item} prev The previously selected item 
6103          */
6104         'changed': true
6105      });
6106     
6107 };
6108
6109 Roo.extend(Roo.bootstrap.nav.Group, Roo.bootstrap.Component,  {
6110     
6111     align: '',
6112     inverse: false,
6113     form: false,
6114     type: 'nav',
6115     navId : '',
6116     // private
6117     pilltype : true,
6118     
6119     navItems : false, 
6120     
6121     getAutoCreate : function()
6122     {
6123         var cfg = Roo.apply({}, Roo.bootstrap.nav.Group.superclass.getAutoCreate.call(this));
6124         
6125         cfg = {
6126             tag : 'ul',
6127             cls: 'nav' 
6128         };
6129         if (Roo.bootstrap.version == 4) {
6130             if (['tabs','pills'].indexOf(this.type) != -1) {
6131                 cfg.cls += ' nav-' + this.type; 
6132             } else {
6133                 // trying to remove so header bar can right align top?
6134                 if (this.parent() && this.parent().xtype != 'NavHeaderbar') {
6135                     // do not use on header bar... 
6136                     cfg.cls += ' navbar-nav';
6137                 }
6138             }
6139             
6140         } else {
6141             if (['tabs','pills'].indexOf(this.type) != -1) {
6142                 cfg.cls += ' nav-' + this.type
6143             } else {
6144                 if (this.type !== 'nav') {
6145                     Roo.log('nav type must be nav/tabs/pills')
6146                 }
6147                 cfg.cls += ' navbar-nav'
6148             }
6149         }
6150         
6151         if (this.parent() && this.parent().sidebar) {
6152             cfg = {
6153                 tag: 'ul',
6154                 cls: 'dashboard-menu sidebar-menu'
6155             };
6156             
6157             return cfg;
6158         }
6159         
6160         if (this.form === true) {
6161             cfg = {
6162                 tag: 'form',
6163                 cls: 'navbar-form form-inline'
6164             };
6165             //nav navbar-right ml-md-auto
6166             if (this.align === 'right') {
6167                 cfg.cls += ' navbar-right ml-md-auto';
6168             } else {
6169                 cfg.cls += ' navbar-left';
6170             }
6171         }
6172         
6173         if (this.align === 'right') {
6174             cfg.cls += ' navbar-right ml-md-auto';
6175         } else {
6176             cfg.cls += ' mr-auto';
6177         }
6178         
6179         if (this.inverse) {
6180             cfg.cls += ' navbar-inverse';
6181             
6182         }
6183         
6184         
6185         return cfg;
6186     },
6187     /**
6188     * sets the active Navigation item
6189     * @param {Roo.bootstrap.nav.Item} the new current navitem
6190     */
6191     setActiveItem : function(item)
6192     {
6193         var prev = false;
6194         Roo.each(this.navItems, function(v){
6195             if (v == item) {
6196                 return ;
6197             }
6198             if (v.isActive()) {
6199                 v.setActive(false, true);
6200                 prev = v;
6201                 
6202             }
6203             
6204         });
6205
6206         item.setActive(true, true);
6207         this.fireEvent('changed', this, item, prev);
6208         
6209         
6210     },
6211     /**
6212     * gets the active Navigation item
6213     * @return {Roo.bootstrap.nav.Item} the current navitem
6214     */
6215     getActive : function()
6216     {
6217         
6218         var prev = false;
6219         Roo.each(this.navItems, function(v){
6220             
6221             if (v.isActive()) {
6222                 prev = v;
6223                 
6224             }
6225             
6226         });
6227         return prev;
6228     },
6229     
6230     indexOfNav : function()
6231     {
6232         
6233         var prev = false;
6234         Roo.each(this.navItems, function(v,i){
6235             
6236             if (v.isActive()) {
6237                 prev = i;
6238                 
6239             }
6240             
6241         });
6242         return prev;
6243     },
6244     /**
6245     * adds a Navigation item
6246     * @param {Roo.bootstrap.nav.Item} the navitem to add
6247     */
6248     addItem : function(cfg)
6249     {
6250         if (this.form && Roo.bootstrap.version == 4) {
6251             cfg.tag = 'div';
6252         }
6253         var cn = new Roo.bootstrap.nav.Item(cfg);
6254         this.register(cn);
6255         cn.parentId = this.id;
6256         cn.onRender(this.el, null);
6257         return cn;
6258     },
6259     /**
6260     * register a Navigation item
6261     * @param {Roo.bootstrap.nav.Item} the navitem to add
6262     */
6263     register : function(item)
6264     {
6265         this.navItems.push( item);
6266         item.navId = this.navId;
6267     
6268     },
6269     
6270     /**
6271     * clear all the Navigation item
6272     */
6273    
6274     clearAll : function()
6275     {
6276         this.navItems = [];
6277         this.el.dom.innerHTML = '';
6278     },
6279     
6280     getNavItem: function(tabId)
6281     {
6282         var ret = false;
6283         Roo.each(this.navItems, function(e) {
6284             if (e.tabId == tabId) {
6285                ret =  e;
6286                return false;
6287             }
6288             return true;
6289             
6290         });
6291         return ret;
6292     },
6293     
6294     setActiveNext : function()
6295     {
6296         var i = this.indexOfNav(this.getActive());
6297         if (i > this.navItems.length) {
6298             return;
6299         }
6300         this.setActiveItem(this.navItems[i+1]);
6301     },
6302     setActivePrev : function()
6303     {
6304         var i = this.indexOfNav(this.getActive());
6305         if (i  < 1) {
6306             return;
6307         }
6308         this.setActiveItem(this.navItems[i-1]);
6309     },
6310     clearWasActive : function(except) {
6311         Roo.each(this.navItems, function(e) {
6312             if (e.tabId != except.tabId && e.was_active) {
6313                e.was_active = false;
6314                return false;
6315             }
6316             return true;
6317             
6318         });
6319     },
6320     getWasActive : function ()
6321     {
6322         var r = false;
6323         Roo.each(this.navItems, function(e) {
6324             if (e.was_active) {
6325                r = e;
6326                return false;
6327             }
6328             return true;
6329             
6330         });
6331         return r;
6332     }
6333     
6334     
6335 });
6336
6337  
6338 Roo.apply(Roo.bootstrap.nav.Group, {
6339     
6340     groups: {},
6341      /**
6342     * register a Navigation Group
6343     * @param {Roo.bootstrap.nav.Group} the navgroup to add
6344     */
6345     register : function(navgrp)
6346     {
6347         this.groups[navgrp.navId] = navgrp;
6348         
6349     },
6350     /**
6351     * fetch a Navigation Group based on the navigation ID
6352     * @param {string} the navgroup to add
6353     * @returns {Roo.bootstrap.nav.Group} the navgroup 
6354     */
6355     get: function(navId) {
6356         if (typeof(this.groups[navId]) == 'undefined') {
6357             return false;
6358             //this.register(new Roo.bootstrap.nav.Group({ navId : navId }));
6359         }
6360         return this.groups[navId] ;
6361     }
6362     
6363     
6364     
6365 });
6366
6367  /**
6368  * @class Roo.bootstrap.nav.Item
6369  * @extends Roo.bootstrap.Component
6370  * @children Roo.bootstrap.Container Roo.bootstrap.Button
6371  * @parent Roo.bootstrap.nav.Group
6372  * @licence LGPL
6373  * Bootstrap Navbar.NavItem class
6374  * 
6375  * @cfg {String} href  link to
6376  * @cfg {String} button_weight (default|primary|secondary|success|info|warning|danger|link|light|dark) default none
6377  * @cfg {Boolean} button_outline show and outlined button
6378  * @cfg {String} html content of button
6379  * @cfg {String} badge text inside badge
6380  * @cfg {String} badgecls (bg-green|bg-red|bg-yellow)the extra classes for the badge
6381  * @cfg {String} glyphicon DEPRICATED - use fa
6382  * @cfg {String} icon DEPRICATED - use fa
6383  * @cfg {String} fa - Fontawsome icon name (can add stuff to it like fa-2x)
6384  * @cfg {Boolean} active Is item active
6385  * @cfg {Boolean} disabled Is item disabled
6386  * @cfg {String} linkcls  Link Class
6387  * @cfg {Boolean} preventDefault (true | false) default false
6388  * @cfg {String} tabId the tab that this item activates.
6389  * @cfg {String} tagtype (a|span) render as a href or span?
6390  * @cfg {Boolean} animateRef (true|false) link to element default false  
6391  * @cfg {Roo.bootstrap.menu.Menu} menu a Menu 
6392   
6393  * @constructor
6394  * Create a new Navbar Item
6395  * @param {Object} config The config object
6396  */
6397 Roo.bootstrap.nav.Item = function(config){
6398     Roo.bootstrap.nav.Item.superclass.constructor.call(this, config);
6399     this.addEvents({
6400         // raw events
6401         /**
6402          * @event click
6403          * The raw click event for the entire grid.
6404          * @param {Roo.EventObject} e
6405          */
6406         "click" : true,
6407          /**
6408             * @event changed
6409             * Fires when the active item active state changes
6410             * @param {Roo.bootstrap.nav.Item} this
6411             * @param {boolean} state the new state
6412              
6413          */
6414         'changed': true,
6415         /**
6416             * @event scrollto
6417             * Fires when scroll to element
6418             * @param {Roo.bootstrap.nav.Item} this
6419             * @param {Object} options
6420             * @param {Roo.EventObject} e
6421              
6422          */
6423         'scrollto': true
6424     });
6425    
6426 };
6427
6428 Roo.extend(Roo.bootstrap.nav.Item, Roo.bootstrap.Component,  {
6429     
6430     href: false,
6431     html: '',
6432     badge: '',
6433     icon: false,
6434     fa : false,
6435     glyphicon: false,
6436     active: false,
6437     preventDefault : false,
6438     tabId : false,
6439     tagtype : 'a',
6440     tag: 'li',
6441     disabled : false,
6442     animateRef : false,
6443     was_active : false,
6444     button_weight : '',
6445     button_outline : false,
6446     linkcls : '',
6447     navLink: false,
6448     
6449     getAutoCreate : function(){
6450          
6451         var cfg = {
6452             tag: this.tag,
6453             cls: 'nav-item'
6454         };
6455         
6456         cfg.cls =  typeof(cfg.cls) == 'undefined'  ? '' : cfg.cls;
6457         
6458         if (this.active) {
6459             cfg.cls +=  ' active' ;
6460         }
6461         if (this.disabled) {
6462             cfg.cls += ' disabled';
6463         }
6464         
6465         // BS4 only?
6466         if (this.button_weight.length) {
6467             cfg.tag = this.href ? 'a' : 'button';
6468             cfg.html = this.html || '';
6469             cfg.cls += ' btn btn' + (this.button_outline ? '-outline' : '') + '-' + this.button_weight;
6470             if (this.href) {
6471                 cfg.href = this.href;
6472             }
6473             if (this.fa) {
6474                 cfg.html = '<i class="fa fas fa-'+this.fa+'"></i> <span class="nav-html">' + this.html + '</span>';
6475             } else {
6476                 cfg.cls += " nav-html";
6477             }
6478             
6479             // menu .. should add dropdown-menu class - so no need for carat..
6480             
6481             if (this.badge !== '') {
6482                  
6483                 cfg.html += ' <span class="badge badge-secondary">' + this.badge + '</span>';
6484             }
6485             return cfg;
6486         }
6487         
6488         if (this.href || this.html || this.glyphicon || this.icon || this.fa) {
6489             cfg.cn = [
6490                 {
6491                     tag: this.tagtype,
6492                     href : this.href || "#",
6493                     html: this.html || '',
6494                     cls : ''
6495                 }
6496             ];
6497             if (this.tagtype == 'a') {
6498                 cfg.cn[0].cls = 'nav-link' +  (this.active ?  ' active'  : '') + ' ' + this.linkcls;
6499         
6500             }
6501             if (this.icon) {
6502                 cfg.cn[0].html = '<i class="'+this.icon+'"></i> <span class="nav-html">' + cfg.cn[0].html + '</span>';
6503             } else  if (this.fa) {
6504                 cfg.cn[0].html = '<i class="fa fas fa-'+this.fa+'"></i> <span class="nav-html">' + cfg.cn[0].html + '</span>';
6505             } else if(this.glyphicon) {
6506                 cfg.cn[0].html = '<span class="glyphicon glyphicon-' + this.glyphicon + '"></span> '  + cfg.cn[0].html;
6507             } else {
6508                 cfg.cn[0].cls += " nav-html";
6509             }
6510             
6511             if (this.menu) {
6512                 cfg.cn[0].html += " <span class='caret'></span>";
6513              
6514             }
6515             
6516             if (this.badge !== '') {
6517                 cfg.cn[0].html += ' <span class="badge badge-secondary">' + this.badge + '</span>';
6518             }
6519         }
6520         
6521         
6522         
6523         return cfg;
6524     },
6525     onRender : function(ct, position)
6526     {
6527        // Roo.log("Call onRender: " + this.xtype);
6528         if (Roo.bootstrap.version == 4 && ct.dom.type != 'ul') {
6529             this.tag = 'div';
6530         }
6531         
6532         var ret = Roo.bootstrap.nav.Item.superclass.onRender.call(this, ct, position);
6533         this.navLink = this.el.select('.nav-link',true).first();
6534         this.htmlEl = this.el.hasClass('nav-html') ? this.el : this.el.select('.nav-html',true).first();
6535         return ret;
6536     },
6537       
6538     
6539     initEvents: function() 
6540     {
6541         if (typeof (this.menu) != 'undefined') {
6542             this.menu.parentType = this.xtype;
6543             this.menu.triggerEl = this.el;
6544             this.menu = this.addxtype(Roo.apply({}, this.menu));
6545         }
6546         
6547         this.el.on('click', this.onClick, this);
6548         
6549         //if(this.tagtype == 'span'){
6550         //    this.el.select('span',true).on('click', this.onClick, this);
6551         //}
6552        
6553         // at this point parent should be available..
6554         this.parent().register(this);
6555     },
6556     
6557     onClick : function(e)
6558     {
6559         if (e.getTarget('.dropdown-menu-item')) {
6560             // did you click on a menu itemm.... - then don't trigger onclick..
6561             return;
6562         }
6563         
6564         if(
6565                 this.preventDefault ||
6566                                 this.href === false ||
6567                 this.href === '#' 
6568         ){
6569             //Roo.log("NavItem - prevent Default?");
6570             e.preventDefault();
6571         }
6572         
6573         if (this.disabled) {
6574             return;
6575         }
6576         
6577         var tg = Roo.bootstrap.TabGroup.get(this.navId);
6578         if (tg && tg.transition) {
6579             Roo.log("waiting for the transitionend");
6580             return;
6581         }
6582         
6583         
6584         
6585         //Roo.log("fire event clicked");
6586         if(this.fireEvent('click', this, e) === false){
6587             return;
6588         };
6589         
6590         if(this.tagtype == 'span'){
6591             return;
6592         }
6593         
6594         //Roo.log(this.href);
6595         var ael = this.el.select('a',true).first();
6596         //Roo.log(ael);
6597         
6598         if(ael && this.animateRef && this.href.indexOf('#') > -1){
6599             //Roo.log(["test:",ael.dom.href.split("#")[0], document.location.toString().split("#")[0]]);
6600             if (ael.dom.href.split("#")[0] != document.location.toString().split("#")[0]) {
6601                 return; // ignore... - it's a 'hash' to another page.
6602             }
6603             Roo.log("NavItem - prevent Default?");
6604             e.preventDefault();
6605             this.scrollToElement(e);
6606         }
6607         
6608         
6609         var p =  this.parent();
6610    
6611         if (['tabs','pills'].indexOf(p.type)!==-1 && p.pilltype) {
6612             if (typeof(p.setActiveItem) !== 'undefined') {
6613                 p.setActiveItem(this);
6614             }
6615         }
6616         
6617         // if parent is a navbarheader....- and link is probably a '#' page ref.. then remove the expanded menu.
6618         if (p.parentType == 'NavHeaderbar' && !this.menu) {
6619             // remove the collapsed menu expand...
6620             p.parent().el.select('.roo-navbar-collapse',true).removeClass('in');  
6621         }
6622     },
6623     
6624     isActive: function () {
6625         return this.active
6626     },
6627     setActive : function(state, fire, is_was_active)
6628     {
6629         if (this.active && !state && this.navId) {
6630             this.was_active = true;
6631             var nv = Roo.bootstrap.nav.Group.get(this.navId);
6632             if (nv) {
6633                 nv.clearWasActive(this);
6634             }
6635             
6636         }
6637         this.active = state;
6638         
6639         if (!state ) {
6640             this.el.removeClass('active');
6641             this.navLink ? this.navLink.removeClass('active') : false;
6642         } else if (!this.el.hasClass('active')) {
6643             
6644             this.el.addClass('active');
6645             if (Roo.bootstrap.version == 4 && this.navLink ) {
6646                 this.navLink.addClass('active');
6647             }
6648             
6649         }
6650         if (fire) {
6651             this.fireEvent('changed', this, state);
6652         }
6653         
6654         // show a panel if it's registered and related..
6655         
6656         if (!this.navId || !this.tabId || !state || is_was_active) {
6657             return;
6658         }
6659         
6660         var tg = Roo.bootstrap.TabGroup.get(this.navId);
6661         if (!tg) {
6662             return;
6663         }
6664         var pan = tg.getPanelByName(this.tabId);
6665         if (!pan) {
6666             return;
6667         }
6668         // if we can not flip to new panel - go back to old nav highlight..
6669         if (false == tg.showPanel(pan)) {
6670             var nv = Roo.bootstrap.nav.Group.get(this.navId);
6671             if (nv) {
6672                 var onav = nv.getWasActive();
6673                 if (onav) {
6674                     onav.setActive(true, false, true);
6675                 }
6676             }
6677             
6678         }
6679         
6680         
6681         
6682     },
6683      // this should not be here...
6684     setDisabled : function(state)
6685     {
6686         this.disabled = state;
6687         if (!state ) {
6688             this.el.removeClass('disabled');
6689         } else if (!this.el.hasClass('disabled')) {
6690             this.el.addClass('disabled');
6691         }
6692         
6693     },
6694     
6695     /**
6696      * Fetch the element to display the tooltip on.
6697      * @return {Roo.Element} defaults to this.el
6698      */
6699     tooltipEl : function()
6700     {
6701         return this.el; //this.tagtype  == 'a' ? this.el  : this.el.select('' + this.tagtype + '', true).first();
6702     },
6703     
6704     scrollToElement : function(e)
6705     {
6706         var c = document.body;
6707         
6708         /*
6709          * Firefox / IE places the overflow at the html level, unless specifically styled to behave differently.
6710          */
6711         if(Roo.isFirefox || Roo.isIE || Roo.isIE11){
6712             c = document.documentElement;
6713         }
6714         
6715         var target = Roo.get(c).select('a[name=' + this.href.split('#')[1] +']', true).first();
6716         
6717         if(!target){
6718             return;
6719         }
6720
6721         var o = target.calcOffsetsTo(c);
6722         
6723         var options = {
6724             target : target,
6725             value : o[1]
6726         };
6727         
6728         this.fireEvent('scrollto', this, options, e);
6729         
6730         Roo.get(c).scrollTo('top', options.value, true);
6731         
6732         return;
6733     },
6734     /**
6735      * Set the HTML (text content) of the item
6736      * @param {string} html  content for the nav item
6737      */
6738     setHtml : function(html)
6739     {
6740         this.html = html;
6741         this.htmlEl.dom.innerHTML = html;
6742         
6743     } 
6744 });
6745  
6746
6747  /*
6748  * - LGPL
6749  *
6750  * sidebar item
6751  *
6752  *  li
6753  *    <span> icon </span>
6754  *    <span> text </span>
6755  *    <span>badge </span>
6756  */
6757
6758 /**
6759  * @class Roo.bootstrap.nav.SidebarItem
6760  * @extends Roo.bootstrap.nav.Item
6761  * Bootstrap Navbar.NavSidebarItem class
6762  * 
6763  * {String} badgeWeight (default|primary|success|info|warning|danger)the extra classes for the badge
6764  * {Boolean} open is the menu open
6765  * {Boolean} buttonView use button as the tigger el rather that a (default false)
6766  * {String} buttonWeight (default|primary|success|info|warning|danger)the extra classes for the button
6767  * {String} buttonSize (sm|md|lg)the extra classes for the button
6768  * {Boolean} showArrow show arrow next to the text (default true)
6769  * @constructor
6770  * Create a new Navbar Button
6771  * @param {Object} config The config object
6772  */
6773 Roo.bootstrap.nav.SidebarItem = function(config){
6774     Roo.bootstrap.nav.SidebarItem.superclass.constructor.call(this, config);
6775     this.addEvents({
6776         // raw events
6777         /**
6778          * @event click
6779          * The raw click event for the entire grid.
6780          * @param {Roo.EventObject} e
6781          */
6782         "click" : true,
6783          /**
6784             * @event changed
6785             * Fires when the active item active state changes
6786             * @param {Roo.bootstrap.nav.SidebarItem} this
6787             * @param {boolean} state the new state
6788              
6789          */
6790         'changed': true
6791     });
6792    
6793 };
6794
6795 Roo.extend(Roo.bootstrap.nav.SidebarItem, Roo.bootstrap.nav.Item,  {
6796     
6797     badgeWeight : 'default',
6798     
6799     open: false,
6800     
6801     buttonView : false,
6802     
6803     buttonWeight : 'default',
6804     
6805     buttonSize : 'md',
6806     
6807     showArrow : true,
6808     
6809     getAutoCreate : function(){
6810         
6811         
6812         var a = {
6813                 tag: 'a',
6814                 href : this.href || '#',
6815                 cls: '',
6816                 html : '',
6817                 cn : []
6818         };
6819         
6820         if(this.buttonView){
6821             a = {
6822                 tag: 'button',
6823                 href : this.href || '#',
6824                 cls: 'btn btn-' + this.buttonWeight + ' btn-' + this.buttonSize + 'roo-button-dropdown-toggle',
6825                 html : this.html,
6826                 cn : []
6827             };
6828         }
6829         
6830         var cfg = {
6831             tag: 'li',
6832             cls: '',
6833             cn: [ a ]
6834         };
6835         
6836         if (this.active) {
6837             cfg.cls += ' active';
6838         }
6839         
6840         if (this.disabled) {
6841             cfg.cls += ' disabled';
6842         }
6843         if (this.open) {
6844             cfg.cls += ' open x-open';
6845         }
6846         // left icon..
6847         if (this.glyphicon || this.icon) {
6848             var c = this.glyphicon  ? ('glyphicon glyphicon-'+this.glyphicon)  : this.icon;
6849             a.cn.push({ tag : 'i', cls : c }) ;
6850         }
6851         
6852         if(!this.buttonView){
6853             var span = {
6854                 tag: 'span',
6855                 html : this.html || ''
6856             };
6857
6858             a.cn.push(span);
6859             
6860         }
6861         
6862         if (this.badge !== '') {
6863             a.cn.push({ tag: 'span',  cls : 'badge pull-right badge-' + this.badgeWeight, html: this.badge }); 
6864         }
6865         
6866         if (this.menu) {
6867             
6868             if(this.showArrow){
6869                 a.cn.push({ tag : 'i', cls : 'glyphicon glyphicon-chevron-down pull-right'});
6870             }
6871             
6872             a.cls += ' dropdown-toggle treeview' ;
6873         }
6874         
6875         return cfg;
6876     },
6877     
6878     initEvents : function()
6879     { 
6880         if (typeof (this.menu) != 'undefined') {
6881             this.menu.parentType = this.xtype;
6882             this.menu.triggerEl = this.el;
6883             this.menu = this.addxtype(Roo.apply({}, this.menu));
6884         }
6885         
6886         this.el.on('click', this.onClick, this);
6887         
6888         if(this.badge !== ''){
6889             this.badgeEl = this.el.select('.badge', true).first().setVisibilityMode(Roo.Element.DISPLAY);
6890         }
6891         
6892     },
6893     
6894     onClick : function(e)
6895     {
6896         if(this.disabled){
6897             e.preventDefault();
6898             return;
6899         }
6900         
6901         if(this.preventDefault){
6902             e.preventDefault();
6903         }
6904         
6905         this.fireEvent('click', this, e);
6906     },
6907     
6908     disable : function()
6909     {
6910         this.setDisabled(true);
6911     },
6912     
6913     enable : function()
6914     {
6915         this.setDisabled(false);
6916     },
6917     
6918     setDisabled : function(state)
6919     {
6920         if(this.disabled == state){
6921             return;
6922         }
6923         
6924         this.disabled = state;
6925         
6926         if (state) {
6927             this.el.addClass('disabled');
6928             return;
6929         }
6930         
6931         this.el.removeClass('disabled');
6932         
6933         return;
6934     },
6935     
6936     setActive : function(state)
6937     {
6938         if(this.active == state){
6939             return;
6940         }
6941         
6942         this.active = state;
6943         
6944         if (state) {
6945             this.el.addClass('active');
6946             return;
6947         }
6948         
6949         this.el.removeClass('active');
6950         
6951         return;
6952     },
6953     
6954     isActive: function () 
6955     {
6956         return this.active;
6957     },
6958     
6959     setBadge : function(str)
6960     {
6961         if(!this.badgeEl){
6962             return;
6963         }
6964         
6965         this.badgeEl.dom.innerHTML = str;
6966     }
6967     
6968    
6969      
6970  
6971 });
6972  
6973
6974  /*
6975  * - LGPL
6976  *
6977  * nav progress bar
6978  * 
6979  */
6980
6981 /**
6982  * @class Roo.bootstrap.nav.ProgressBar
6983  * @extends Roo.bootstrap.Component
6984  * @children Roo.bootstrap.nav.ProgressBarItem
6985  * Bootstrap NavProgressBar class
6986  * 
6987  * @constructor
6988  * Create a new nav progress bar - a bar indicating step along a process
6989  * @param {Object} config The config object
6990  */
6991
6992 Roo.bootstrap.nav.ProgressBar = function(config){
6993     Roo.bootstrap.nav.ProgressBar.superclass.constructor.call(this, config);
6994
6995     this.bullets = this.bullets || [];
6996    
6997 //    Roo.bootstrap.nav.ProgressBar.register(this);
6998      this.addEvents({
6999         /**
7000              * @event changed
7001              * Fires when the active item changes
7002              * @param {Roo.bootstrap.nav.ProgressBar} this
7003              * @param {Roo.bootstrap.nav.ProgressItem} selected The item selected
7004              * @param {Roo.bootstrap.nav.ProgressItem} prev The previously selected item 
7005          */
7006         'changed': true
7007      });
7008     
7009 };
7010
7011 Roo.extend(Roo.bootstrap.nav.ProgressBar, Roo.bootstrap.Component,  {
7012     /**
7013      * @cfg {Roo.bootstrap.nav.ProgressItem} NavProgressBar:bullets[]
7014      * Bullets for the Nav Progress bar for the toolbar
7015      */
7016     bullets : [],
7017     barItems : [],
7018     
7019     getAutoCreate : function()
7020     {
7021         var cfg = Roo.apply({}, Roo.bootstrap.nav.ProgressBar.superclass.getAutoCreate.call(this));
7022         
7023         cfg = {
7024             tag : 'div',
7025             cls : 'roo-navigation-bar-group',
7026             cn : [
7027                 {
7028                     tag : 'div',
7029                     cls : 'roo-navigation-top-bar'
7030                 },
7031                 {
7032                     tag : 'div',
7033                     cls : 'roo-navigation-bullets-bar',
7034                     cn : [
7035                         {
7036                             tag : 'ul',
7037                             cls : 'roo-navigation-bar'
7038                         }
7039                     ]
7040                 },
7041                 
7042                 {
7043                     tag : 'div',
7044                     cls : 'roo-navigation-bottom-bar'
7045                 }
7046             ]
7047             
7048         };
7049         
7050         return cfg;
7051         
7052     },
7053     
7054     initEvents: function() 
7055     {
7056         
7057     },
7058     
7059     onRender : function(ct, position) 
7060     {
7061         Roo.bootstrap.nav.ProgressBar.superclass.onRender.call(this, ct, position);
7062         
7063         if(this.bullets.length){
7064             Roo.each(this.bullets, function(b){
7065                this.addItem(b);
7066             }, this);
7067         }
7068         
7069         this.format();
7070         
7071     },
7072     
7073     addItem : function(cfg)
7074     {
7075         var item = new Roo.bootstrap.nav.ProgressItem(cfg);
7076         
7077         item.parentId = this.id;
7078         item.render(this.el.select('.roo-navigation-bar', true).first(), null);
7079         
7080         if(cfg.html){
7081             var top = new Roo.bootstrap.Element({
7082                 tag : 'div',
7083                 cls : 'roo-navigation-bar-text'
7084             });
7085             
7086             var bottom = new Roo.bootstrap.Element({
7087                 tag : 'div',
7088                 cls : 'roo-navigation-bar-text'
7089             });
7090             
7091             top.onRender(this.el.select('.roo-navigation-top-bar', true).first(), null);
7092             bottom.onRender(this.el.select('.roo-navigation-bottom-bar', true).first(), null);
7093             
7094             var topText = new Roo.bootstrap.Element({
7095                 tag : 'span',
7096                 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? cfg.html : ''
7097             });
7098             
7099             var bottomText = new Roo.bootstrap.Element({
7100                 tag : 'span',
7101                 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? '' : cfg.html
7102             });
7103             
7104             topText.onRender(top.el, null);
7105             bottomText.onRender(bottom.el, null);
7106             
7107             item.topEl = top;
7108             item.bottomEl = bottom;
7109         }
7110         
7111         this.barItems.push(item);
7112         
7113         return item;
7114     },
7115     
7116     getActive : function()
7117     {
7118         var active = false;
7119         
7120         Roo.each(this.barItems, function(v){
7121             
7122             if (!v.isActive()) {
7123                 return;
7124             }
7125             
7126             active = v;
7127             return false;
7128             
7129         });
7130         
7131         return active;
7132     },
7133     
7134     setActiveItem : function(item)
7135     {
7136         var prev = false;
7137         
7138         Roo.each(this.barItems, function(v){
7139             if (v.rid == item.rid) {
7140                 return ;
7141             }
7142             
7143             if (v.isActive()) {
7144                 v.setActive(false);
7145                 prev = v;
7146             }
7147         });
7148
7149         item.setActive(true);
7150         
7151         this.fireEvent('changed', this, item, prev);
7152     },
7153     
7154     getBarItem: function(rid)
7155     {
7156         var ret = false;
7157         
7158         Roo.each(this.barItems, function(e) {
7159             if (e.rid != rid) {
7160                 return;
7161             }
7162             
7163             ret =  e;
7164             return false;
7165         });
7166         
7167         return ret;
7168     },
7169     
7170     indexOfItem : function(item)
7171     {
7172         var index = false;
7173         
7174         Roo.each(this.barItems, function(v, i){
7175             
7176             if (v.rid != item.rid) {
7177                 return;
7178             }
7179             
7180             index = i;
7181             return false
7182         });
7183         
7184         return index;
7185     },
7186     
7187     setActiveNext : function()
7188     {
7189         var i = this.indexOfItem(this.getActive());
7190         
7191         if (i > this.barItems.length) {
7192             return;
7193         }
7194         
7195         this.setActiveItem(this.barItems[i+1]);
7196     },
7197     
7198     setActivePrev : function()
7199     {
7200         var i = this.indexOfItem(this.getActive());
7201         
7202         if (i  < 1) {
7203             return;
7204         }
7205         
7206         this.setActiveItem(this.barItems[i-1]);
7207     },
7208     
7209     format : function()
7210     {
7211         if(!this.barItems.length){
7212             return;
7213         }
7214      
7215         var width = 100 / this.barItems.length;
7216         
7217         Roo.each(this.barItems, function(i){
7218             i.el.setStyle('width', width + '%');
7219             i.topEl.el.setStyle('width', width + '%');
7220             i.bottomEl.el.setStyle('width', width + '%');
7221         }, this);
7222         
7223     }
7224     
7225 });
7226 /*
7227  * - LGPL
7228  *
7229  * Nav Progress Item
7230  * 
7231  */
7232
7233 /**
7234  * @class Roo.bootstrap.nav.ProgressBarItem
7235  * @extends Roo.bootstrap.Component
7236  * Bootstrap NavProgressBarItem class
7237  * @cfg {String} rid the reference id
7238  * @cfg {Boolean} active (true|false) Is item active default false
7239  * @cfg {Boolean} disabled (true|false) Is item active default false
7240  * @cfg {String} html
7241  * @cfg {String} position (top|bottom) text position default bottom
7242  * @cfg {String} icon show icon instead of number
7243  * 
7244  * @constructor
7245  * Create a new NavProgressBarItem
7246  * @param {Object} config The config object
7247  */
7248 Roo.bootstrap.nav.ProgressBarItem = function(config){
7249     Roo.bootstrap.nav.ProgressBarItem.superclass.constructor.call(this, config);
7250     this.addEvents({
7251         // raw events
7252         /**
7253          * @event click
7254          * The raw click event for the entire grid.
7255          * @param {Roo.bootstrap.nav.ProgressBarItem} this
7256          * @param {Roo.EventObject} e
7257          */
7258         "click" : true
7259     });
7260    
7261 };
7262
7263 Roo.extend(Roo.bootstrap.nav.ProgressBarItem, Roo.bootstrap.Component,  {
7264     
7265     rid : '',
7266     active : false,
7267     disabled : false,
7268     html : '',
7269     position : 'bottom',
7270     icon : false,
7271     
7272     getAutoCreate : function()
7273     {
7274         var iconCls = 'roo-navigation-bar-item-icon';
7275         
7276         iconCls += ((this.icon) ? (' ' + this.icon) : (' step-number')) ;
7277         
7278         var cfg = {
7279             tag: 'li',
7280             cls: 'roo-navigation-bar-item',
7281             cn : [
7282                 {
7283                     tag : 'i',
7284                     cls : iconCls
7285                 }
7286             ]
7287         };
7288         
7289         if(this.active){
7290             cfg.cls += ' active';
7291         }
7292         if(this.disabled){
7293             cfg.cls += ' disabled';
7294         }
7295         
7296         return cfg;
7297     },
7298     
7299     disable : function()
7300     {
7301         this.setDisabled(true);
7302     },
7303     
7304     enable : function()
7305     {
7306         this.setDisabled(false);
7307     },
7308     
7309     initEvents: function() 
7310     {
7311         this.iconEl = this.el.select('.roo-navigation-bar-item-icon', true).first();
7312         
7313         this.iconEl.on('click', this.onClick, this);
7314     },
7315     
7316     onClick : function(e)
7317     {
7318         e.preventDefault();
7319         
7320         if(this.disabled){
7321             return;
7322         }
7323         
7324         if(this.fireEvent('click', this, e) === false){
7325             return;
7326         };
7327         
7328         this.parent().setActiveItem(this);
7329     },
7330     
7331     isActive: function () 
7332     {
7333         return this.active;
7334     },
7335     
7336     setActive : function(state)
7337     {
7338         if(this.active == state){
7339             return;
7340         }
7341         
7342         this.active = state;
7343         
7344         if (state) {
7345             this.el.addClass('active');
7346             return;
7347         }
7348         
7349         this.el.removeClass('active');
7350         
7351         return;
7352     },
7353     
7354     setDisabled : function(state)
7355     {
7356         if(this.disabled == state){
7357             return;
7358         }
7359         
7360         this.disabled = state;
7361         
7362         if (state) {
7363             this.el.addClass('disabled');
7364             return;
7365         }
7366         
7367         this.el.removeClass('disabled');
7368     },
7369     
7370     tooltipEl : function()
7371     {
7372         return this.el.select('.roo-navigation-bar-item-icon', true).first();;
7373     }
7374 });
7375  
7376
7377  /*
7378  * - LGPL
7379  *
7380  *  Breadcrumb Nav
7381  * 
7382  */
7383 Roo.namespace('Roo.bootstrap.breadcrumb');
7384
7385
7386 /**
7387  * @class Roo.bootstrap.breadcrumb.Nav
7388  * @extends Roo.bootstrap.Component
7389  * Bootstrap Breadcrumb Nav Class
7390  *  
7391  * @children Roo.bootstrap.breadcrumb.Item
7392  * 
7393  * @constructor
7394  * Create a new breadcrumb.Nav
7395  * @param {Object} config The config object
7396  */
7397
7398
7399 Roo.bootstrap.breadcrumb.Nav = function(config){
7400     Roo.bootstrap.breadcrumb.Nav.superclass.constructor.call(this, config);
7401     
7402     
7403 };
7404
7405 Roo.extend(Roo.bootstrap.breadcrumb.Nav, Roo.bootstrap.Component,  {
7406     
7407     getAutoCreate : function()
7408     {
7409
7410         var cfg = {
7411             tag: 'nav',
7412             cn : [
7413                 {
7414                     tag : 'ol',
7415                     cls : 'breadcrumb'
7416                 }
7417             ]
7418             
7419         };
7420           
7421         return cfg;
7422     },
7423     
7424     initEvents: function()
7425     {
7426         this.olEl = this.el.select('ol',true).first();    
7427     },
7428     getChildContainer : function()
7429     {
7430         return this.olEl;  
7431     }
7432     
7433 });
7434
7435  /*
7436  * - LGPL
7437  *
7438  *  Breadcrumb Item
7439  * 
7440  */
7441
7442
7443 /**
7444  * @class Roo.bootstrap.breadcrumb.Nav
7445  * @extends Roo.bootstrap.Component
7446  * @children Roo.bootstrap.Component
7447  * @parent Roo.bootstrap.breadcrumb.Nav
7448  * Bootstrap Breadcrumb Nav Class
7449  *  
7450  * 
7451  * @cfg {String} html the content of the link.
7452  * @cfg {String} href where it links to if '#' is used the link will be handled by onClick.
7453  * @cfg {Boolean} active is it active
7454
7455  * 
7456  * @constructor
7457  * Create a new breadcrumb.Nav
7458  * @param {Object} config The config object
7459  */
7460
7461 Roo.bootstrap.breadcrumb.Item = function(config){
7462     Roo.bootstrap.breadcrumb.Item.superclass.constructor.call(this, config);
7463     this.addEvents({
7464         // img events
7465         /**
7466          * @event click
7467          * The img click event for the img.
7468          * @param {Roo.EventObject} e
7469          */
7470         "click" : true
7471     });
7472     
7473 };
7474
7475 Roo.extend(Roo.bootstrap.breadcrumb.Item, Roo.bootstrap.Component,  {
7476     
7477     href: false,
7478     html : '',
7479     
7480     getAutoCreate : function()
7481     {
7482
7483         var cfg = {
7484             tag: 'li',
7485             cls : 'breadcrumb-item' + (this.active ? ' active' : '')
7486         };
7487         if (this.href !== false) {
7488             cfg.cn = [{
7489                 tag : 'a',
7490                 href : this.href,
7491                 html : this.html
7492             }];
7493         } else {
7494             cfg.html = this.html;
7495         }
7496         
7497         return cfg;
7498     },
7499     
7500     initEvents: function()
7501     {
7502         if (this.href) {
7503             this.el.select('a', true).first().on('click',this.onClick, this)
7504         }
7505         
7506     },
7507     onClick : function(e)
7508     {
7509         e.preventDefault();
7510         this.fireEvent('click',this,  e);
7511     }
7512     
7513 });
7514
7515  /*
7516  * - LGPL
7517  *
7518  * row
7519  * 
7520  */
7521
7522 /**
7523  * @class Roo.bootstrap.Row
7524  * @extends Roo.bootstrap.Component
7525  * @children Roo.bootstrap.Component
7526  * Bootstrap Row class (contains columns...)
7527  * 
7528  * @constructor
7529  * Create a new Row
7530  * @param {Object} config The config object
7531  */
7532
7533 Roo.bootstrap.Row = function(config){
7534     Roo.bootstrap.Row.superclass.constructor.call(this, config);
7535 };
7536
7537 Roo.extend(Roo.bootstrap.Row, Roo.bootstrap.Component,  {
7538     
7539     getAutoCreate : function(){
7540        return {
7541             cls: 'row clearfix'
7542        };
7543     }
7544     
7545     
7546 });
7547
7548  
7549
7550  /*
7551  * - LGPL
7552  *
7553  * pagination
7554  * 
7555  */
7556
7557 /**
7558  * @class Roo.bootstrap.Pagination
7559  * @extends Roo.bootstrap.Component
7560  * @children Roo.bootstrap.Pagination
7561  * Bootstrap Pagination class
7562  * 
7563  * @cfg {String} size (xs|sm|md|lg|xl)
7564  * @cfg {Boolean} inverse 
7565  * 
7566  * @constructor
7567  * Create a new Pagination
7568  * @param {Object} config The config object
7569  */
7570
7571 Roo.bootstrap.Pagination = function(config){
7572     Roo.bootstrap.Pagination.superclass.constructor.call(this, config);
7573 };
7574
7575 Roo.extend(Roo.bootstrap.Pagination, Roo.bootstrap.Component,  {
7576     
7577     cls: false,
7578     size: false,
7579     inverse: false,
7580     
7581     getAutoCreate : function(){
7582         var cfg = {
7583             tag: 'ul',
7584                 cls: 'pagination'
7585         };
7586         if (this.inverse) {
7587             cfg.cls += ' inverse';
7588         }
7589         if (this.html) {
7590             cfg.html=this.html;
7591         }
7592         if (this.cls) {
7593             cfg.cls += " " + this.cls;
7594         }
7595         return cfg;
7596     }
7597    
7598 });
7599
7600  
7601
7602  /*
7603  * - LGPL
7604  *
7605  * Pagination item
7606  * 
7607  */
7608
7609
7610 /**
7611  * @class Roo.bootstrap.PaginationItem
7612  * @extends Roo.bootstrap.Component
7613  * Bootstrap PaginationItem class
7614  * @cfg {String} html text
7615  * @cfg {String} href the link
7616  * @cfg {Boolean} preventDefault (true | false) default true
7617  * @cfg {Boolean} active (true | false) default false
7618  * @cfg {Boolean} disabled default false
7619  * 
7620  * 
7621  * @constructor
7622  * Create a new PaginationItem
7623  * @param {Object} config The config object
7624  */
7625
7626
7627 Roo.bootstrap.PaginationItem = function(config){
7628     Roo.bootstrap.PaginationItem.superclass.constructor.call(this, config);
7629     this.addEvents({
7630         // raw events
7631         /**
7632          * @event click
7633          * The raw click event for the entire grid.
7634          * @param {Roo.EventObject} e
7635          */
7636         "click" : true
7637     });
7638 };
7639
7640 Roo.extend(Roo.bootstrap.PaginationItem, Roo.bootstrap.Component,  {
7641     
7642     href : false,
7643     html : false,
7644     preventDefault: true,
7645     active : false,
7646     cls : false,
7647     disabled: false,
7648     
7649     getAutoCreate : function(){
7650         var cfg= {
7651             tag: 'li',
7652             cn: [
7653                 {
7654                     tag : 'a',
7655                     href : this.href ? this.href : '#',
7656                     html : this.html ? this.html : ''
7657                 }
7658             ]
7659         };
7660         
7661         if(this.cls){
7662             cfg.cls = this.cls;
7663         }
7664         
7665         if(this.disabled){
7666             cfg.cls = typeof(cfg.cls) !== 'undefined' ? cfg.cls + ' disabled' : 'disabled';
7667         }
7668         
7669         if(this.active){
7670             cfg.cls = typeof(cfg.cls) !== 'undefined' ? cfg.cls + ' active' : 'active';
7671         }
7672         
7673         return cfg;
7674     },
7675     
7676     initEvents: function() {
7677         
7678         this.el.on('click', this.onClick, this);
7679         
7680     },
7681     onClick : function(e)
7682     {
7683         Roo.log('PaginationItem on click ');
7684         if(this.preventDefault){
7685             e.preventDefault();
7686         }
7687         
7688         if(this.disabled){
7689             return;
7690         }
7691         
7692         this.fireEvent('click', this, e);
7693     }
7694    
7695 });
7696
7697  
7698
7699  /*
7700  * - LGPL
7701  *
7702  * slider
7703  * 
7704  */
7705
7706
7707 /**
7708  * @class Roo.bootstrap.Slider
7709  * @extends Roo.bootstrap.Component
7710  * Bootstrap Slider class
7711  *    
7712  * @constructor
7713  * Create a new Slider
7714  * @param {Object} config The config object
7715  */
7716
7717 Roo.bootstrap.Slider = function(config){
7718     Roo.bootstrap.Slider.superclass.constructor.call(this, config);
7719 };
7720
7721 Roo.extend(Roo.bootstrap.Slider, Roo.bootstrap.Component,  {
7722     
7723     getAutoCreate : function(){
7724         
7725         var cfg = {
7726             tag: 'div',
7727             cls: 'slider slider-sample1 vertical-handler ui-slider ui-slider-horizontal ui-widget ui-widget-content ui-corner-all',
7728             cn: [
7729                 {
7730                     tag: 'a',
7731                     cls: 'ui-slider-handle ui-state-default ui-corner-all'
7732                 }
7733             ]
7734         };
7735         
7736         return cfg;
7737     }
7738    
7739 });
7740
7741  /*
7742  * Based on:
7743  * Ext JS Library 1.1.1
7744  * Copyright(c) 2006-2007, Ext JS, LLC.
7745  *
7746  * Originally Released Under LGPL - original licence link has changed is not relivant.
7747  *
7748  * Fork - LGPL
7749  * <script type="text/javascript">
7750  */
7751  /**
7752  * @extends Roo.dd.DDProxy
7753  * @class Roo.grid.SplitDragZone
7754  * Support for Column Header resizing
7755  * @constructor
7756  * @param {Object} config
7757  */
7758 // private
7759 // This is a support class used internally by the Grid components
7760 Roo.grid.SplitDragZone = function(grid, hd, hd2){
7761     this.grid = grid;
7762     this.view = grid.getView();
7763     this.proxy = this.view.resizeProxy;
7764     Roo.grid.SplitDragZone.superclass.constructor.call(
7765         this,
7766         hd, // ID
7767         "gridSplitters" + this.grid.getGridEl().id, // SGROUP
7768         {  // CONFIG
7769             dragElId : Roo.id(this.proxy.dom),
7770             resizeFrame:false
7771         }
7772     );
7773     
7774     this.setHandleElId(Roo.id(hd));
7775     if (hd2 !== false) {
7776         this.setOuterHandleElId(Roo.id(hd2));
7777     }
7778     
7779     this.scroll = false;
7780 };
7781 Roo.extend(Roo.grid.SplitDragZone, Roo.dd.DDProxy, {
7782     fly: Roo.Element.fly,
7783
7784     b4StartDrag : function(x, y){
7785         this.view.headersDisabled = true;
7786         var h = this.view.mainWrap ? this.view.mainWrap.getHeight() : (
7787                     this.view.headEl.getHeight() + this.view.bodyEl.getHeight()
7788         );
7789         this.proxy.setHeight(h);
7790         
7791         // for old system colWidth really stored the actual width?
7792         // in bootstrap we tried using xs/ms/etc.. to do % sizing?
7793         // which in reality did not work.. - it worked only for fixed sizes
7794         // for resizable we need to use actual sizes.
7795         var w = this.cm.getColumnWidth(this.cellIndex);
7796         if (!this.view.mainWrap) {
7797             // bootstrap.
7798             w = this.view.getHeaderIndex(this.cellIndex).getWidth();
7799         }
7800         
7801         
7802         
7803         // this was w-this.grid.minColumnWidth;
7804         // doesnt really make sense? - w = thie curren width or the rendered one?
7805         var minw = Math.max(w-this.grid.minColumnWidth, 0);
7806         this.resetConstraints();
7807         this.setXConstraint(minw, 1000);
7808         this.setYConstraint(0, 0);
7809         this.minX = x - minw;
7810         this.maxX = x + 1000;
7811         this.startPos = x;
7812         if (!this.view.mainWrap) { // this is Bootstrap code..
7813             this.getDragEl().style.display='block';
7814         }
7815         
7816         Roo.dd.DDProxy.prototype.b4StartDrag.call(this, x, y);
7817     },
7818
7819
7820     handleMouseDown : function(e){
7821         ev = Roo.EventObject.setEvent(e);
7822         var t = this.fly(ev.getTarget());
7823         if(t.hasClass("x-grid-split")){
7824             this.cellIndex = this.view.getCellIndex(t.dom);
7825             this.split = t.dom;
7826             this.cm = this.grid.colModel;
7827             if(this.cm.isResizable(this.cellIndex) && !this.cm.isFixed(this.cellIndex)){
7828                 Roo.grid.SplitDragZone.superclass.handleMouseDown.apply(this, arguments);
7829             }
7830         }
7831     },
7832
7833     endDrag : function(e){
7834         this.view.headersDisabled = false;
7835         var endX = Math.max(this.minX, Roo.lib.Event.getPageX(e));
7836         var diff = endX - this.startPos;
7837         // 
7838         var w = this.cm.getColumnWidth(this.cellIndex);
7839         if (!this.view.mainWrap) {
7840             w = 0;
7841         }
7842         this.view.onColumnSplitterMoved(this.cellIndex, w+diff);
7843     },
7844
7845     autoOffset : function(){
7846         this.setDelta(0,0);
7847     }
7848 });/*
7849  * Based on:
7850  * Ext JS Library 1.1.1
7851  * Copyright(c) 2006-2007, Ext JS, LLC.
7852  *
7853  * Originally Released Under LGPL - original licence link has changed is not relivant.
7854  *
7855  * Fork - LGPL
7856  * <script type="text/javascript">
7857  */
7858
7859 /**
7860  * @class Roo.grid.AbstractSelectionModel
7861  * @extends Roo.util.Observable
7862  * @abstract
7863  * Abstract base class for grid SelectionModels.  It provides the interface that should be
7864  * implemented by descendant classes.  This class should not be directly instantiated.
7865  * @constructor
7866  */
7867 Roo.grid.AbstractSelectionModel = function(){
7868     this.locked = false;
7869     Roo.grid.AbstractSelectionModel.superclass.constructor.call(this);
7870 };
7871
7872 Roo.extend(Roo.grid.AbstractSelectionModel, Roo.util.Observable,  {
7873     /** @ignore Called by the grid automatically. Do not call directly. */
7874     init : function(grid){
7875         this.grid = grid;
7876         this.initEvents();
7877     },
7878
7879     /**
7880      * Locks the selections.
7881      */
7882     lock : function(){
7883         this.locked = true;
7884     },
7885
7886     /**
7887      * Unlocks the selections.
7888      */
7889     unlock : function(){
7890         this.locked = false;
7891     },
7892
7893     /**
7894      * Returns true if the selections are locked.
7895      * @return {Boolean}
7896      */
7897     isLocked : function(){
7898         return this.locked;
7899     }
7900 });/*
7901  * Based on:
7902  * Ext JS Library 1.1.1
7903  * Copyright(c) 2006-2007, Ext JS, LLC.
7904  *
7905  * Originally Released Under LGPL - original licence link has changed is not relivant.
7906  *
7907  * Fork - LGPL
7908  * <script type="text/javascript">
7909  */
7910 /**
7911  * @extends Roo.grid.AbstractSelectionModel
7912  * @class Roo.grid.RowSelectionModel
7913  * The default SelectionModel used by {@link Roo.grid.Grid}.
7914  * It supports multiple selections and keyboard selection/navigation. 
7915  * @constructor
7916  * @param {Object} config
7917  */
7918 Roo.grid.RowSelectionModel = function(config){
7919     Roo.apply(this, config);
7920     this.selections = new Roo.util.MixedCollection(false, function(o){
7921         return o.id;
7922     });
7923
7924     this.last = false;
7925     this.lastActive = false;
7926
7927     this.addEvents({
7928         /**
7929         * @event selectionchange
7930         * Fires when the selection changes
7931         * @param {SelectionModel} this
7932         */
7933        "selectionchange" : true,
7934        /**
7935         * @event afterselectionchange
7936         * Fires after the selection changes (eg. by key press or clicking)
7937         * @param {SelectionModel} this
7938         */
7939        "afterselectionchange" : true,
7940        /**
7941         * @event beforerowselect
7942         * Fires when a row is selected being selected, return false to cancel.
7943         * @param {SelectionModel} this
7944         * @param {Number} rowIndex The selected index
7945         * @param {Boolean} keepExisting False if other selections will be cleared
7946         */
7947        "beforerowselect" : true,
7948        /**
7949         * @event rowselect
7950         * Fires when a row is selected.
7951         * @param {SelectionModel} this
7952         * @param {Number} rowIndex The selected index
7953         * @param {Roo.data.Record} r The record
7954         */
7955        "rowselect" : true,
7956        /**
7957         * @event rowdeselect
7958         * Fires when a row is deselected.
7959         * @param {SelectionModel} this
7960         * @param {Number} rowIndex The selected index
7961         */
7962         "rowdeselect" : true
7963     });
7964     Roo.grid.RowSelectionModel.superclass.constructor.call(this);
7965     this.locked = false;
7966 };
7967
7968 Roo.extend(Roo.grid.RowSelectionModel, Roo.grid.AbstractSelectionModel,  {
7969     /**
7970      * @cfg {Boolean} singleSelect
7971      * True to allow selection of only one row at a time (defaults to false)
7972      */
7973     singleSelect : false,
7974
7975     // private
7976     initEvents : function(){
7977
7978         if(!this.grid.enableDragDrop && !this.grid.enableDrag){
7979             this.grid.on("mousedown", this.handleMouseDown, this);
7980         }else{ // allow click to work like normal
7981             this.grid.on("rowclick", this.handleDragableRowClick, this);
7982         }
7983         // bootstrap does not have a view..
7984         var view = this.grid.view ? this.grid.view : this.grid;
7985         this.rowNav = new Roo.KeyNav(this.grid.getGridEl(), {
7986             "up" : function(e){
7987                 if(!e.shiftKey){
7988                     this.selectPrevious(e.shiftKey);
7989                 }else if(this.last !== false && this.lastActive !== false){
7990                     var last = this.last;
7991                     this.selectRange(this.last,  this.lastActive-1);
7992                     view.focusRow(this.lastActive);
7993                     if(last !== false){
7994                         this.last = last;
7995                     }
7996                 }else{
7997                     this.selectFirstRow();
7998                 }
7999                 this.fireEvent("afterselectionchange", this);
8000             },
8001             "down" : function(e){
8002                 if(!e.shiftKey){
8003                     this.selectNext(e.shiftKey);
8004                 }else if(this.last !== false && this.lastActive !== false){
8005                     var last = this.last;
8006                     this.selectRange(this.last,  this.lastActive+1);
8007                     view.focusRow(this.lastActive);
8008                     if(last !== false){
8009                         this.last = last;
8010                     }
8011                 }else{
8012                     this.selectFirstRow();
8013                 }
8014                 this.fireEvent("afterselectionchange", this);
8015             },
8016             scope: this
8017         });
8018
8019          
8020         view.on("refresh", this.onRefresh, this);
8021         view.on("rowupdated", this.onRowUpdated, this);
8022         view.on("rowremoved", this.onRemove, this);
8023     },
8024
8025     // private
8026     onRefresh : function(){
8027         var ds = this.grid.ds, i, v = this.grid.view;
8028         var s = this.selections;
8029         s.each(function(r){
8030             if((i = ds.indexOfId(r.id)) != -1){
8031                 v.onRowSelect(i);
8032                 s.add(ds.getAt(i)); // updating the selection relate data
8033             }else{
8034                 s.remove(r);
8035             }
8036         });
8037     },
8038
8039     // private
8040     onRemove : function(v, index, r){
8041         this.selections.remove(r);
8042     },
8043
8044     // private
8045     onRowUpdated : function(v, index, r){
8046         if(this.isSelected(r)){
8047             v.onRowSelect(index);
8048         }
8049     },
8050
8051     /**
8052      * Select records.
8053      * @param {Array} records The records to select
8054      * @param {Boolean} keepExisting (optional) True to keep existing selections
8055      */
8056     selectRecords : function(records, keepExisting){
8057         if(!keepExisting){
8058             this.clearSelections();
8059         }
8060         var ds = this.grid.ds;
8061         for(var i = 0, len = records.length; i < len; i++){
8062             this.selectRow(ds.indexOf(records[i]), true);
8063         }
8064     },
8065
8066     /**
8067      * Gets the number of selected rows.
8068      * @return {Number}
8069      */
8070     getCount : function(){
8071         return this.selections.length;
8072     },
8073
8074     /**
8075      * Selects the first row in the grid.
8076      */
8077     selectFirstRow : function(){
8078         this.selectRow(0);
8079     },
8080
8081     /**
8082      * Select the last row.
8083      * @param {Boolean} keepExisting (optional) True to keep existing selections
8084      */
8085     selectLastRow : function(keepExisting){
8086         this.selectRow(this.grid.ds.getCount() - 1, keepExisting);
8087     },
8088
8089     /**
8090      * Selects the row immediately following the last selected row.
8091      * @param {Boolean} keepExisting (optional) True to keep existing selections
8092      */
8093     selectNext : function(keepExisting){
8094         if(this.last !== false && (this.last+1) < this.grid.ds.getCount()){
8095             this.selectRow(this.last+1, keepExisting);
8096             var view = this.grid.view ? this.grid.view : this.grid;
8097             view.focusRow(this.last);
8098         }
8099     },
8100
8101     /**
8102      * Selects the row that precedes the last selected row.
8103      * @param {Boolean} keepExisting (optional) True to keep existing selections
8104      */
8105     selectPrevious : function(keepExisting){
8106         if(this.last){
8107             this.selectRow(this.last-1, keepExisting);
8108             var view = this.grid.view ? this.grid.view : this.grid;
8109             view.focusRow(this.last);
8110         }
8111     },
8112
8113     /**
8114      * Returns the selected records
8115      * @return {Array} Array of selected records
8116      */
8117     getSelections : function(){
8118         return [].concat(this.selections.items);
8119     },
8120
8121     /**
8122      * Returns the first selected record.
8123      * @return {Record}
8124      */
8125     getSelected : function(){
8126         return this.selections.itemAt(0);
8127     },
8128
8129
8130     /**
8131      * Clears all selections.
8132      */
8133     clearSelections : function(fast){
8134         if(this.locked) {
8135             return;
8136         }
8137         if(fast !== true){
8138             var ds = this.grid.ds;
8139             var s = this.selections;
8140             s.each(function(r){
8141                 this.deselectRow(ds.indexOfId(r.id));
8142             }, this);
8143             s.clear();
8144         }else{
8145             this.selections.clear();
8146         }
8147         this.last = false;
8148     },
8149
8150
8151     /**
8152      * Selects all rows.
8153      */
8154     selectAll : function(){
8155         if(this.locked) {
8156             return;
8157         }
8158         this.selections.clear();
8159         for(var i = 0, len = this.grid.ds.getCount(); i < len; i++){
8160             this.selectRow(i, true);
8161         }
8162     },
8163
8164     /**
8165      * Returns True if there is a selection.
8166      * @return {Boolean}
8167      */
8168     hasSelection : function(){
8169         return this.selections.length > 0;
8170     },
8171
8172     /**
8173      * Returns True if the specified row is selected.
8174      * @param {Number/Record} record The record or index of the record to check
8175      * @return {Boolean}
8176      */
8177     isSelected : function(index){
8178         var r = typeof index == "number" ? this.grid.ds.getAt(index) : index;
8179         return (r && this.selections.key(r.id) ? true : false);
8180     },
8181
8182     /**
8183      * Returns True if the specified record id is selected.
8184      * @param {String} id The id of record to check
8185      * @return {Boolean}
8186      */
8187     isIdSelected : function(id){
8188         return (this.selections.key(id) ? true : false);
8189     },
8190
8191     // private
8192     handleMouseDown : function(e, t)
8193     {
8194         var view = this.grid.view ? this.grid.view : this.grid;
8195         var rowIndex;
8196         if(this.isLocked() || (rowIndex = view.findRowIndex(t)) === false){
8197             return;
8198         };
8199         if(e.shiftKey && this.last !== false){
8200             var last = this.last;
8201             this.selectRange(last, rowIndex, e.ctrlKey);
8202             this.last = last; // reset the last
8203             view.focusRow(rowIndex);
8204         }else{
8205             var isSelected = this.isSelected(rowIndex);
8206             if(e.button !== 0 && isSelected){
8207                 view.focusRow(rowIndex);
8208             }else if(e.ctrlKey && isSelected){
8209                 this.deselectRow(rowIndex);
8210             }else if(!isSelected){
8211                 this.selectRow(rowIndex, e.button === 0 && (e.ctrlKey || e.shiftKey));
8212                 view.focusRow(rowIndex);
8213             }
8214         }
8215         this.fireEvent("afterselectionchange", this);
8216     },
8217     // private
8218     handleDragableRowClick :  function(grid, rowIndex, e) 
8219     {
8220         if(e.button === 0 && !e.shiftKey && !e.ctrlKey) {
8221             this.selectRow(rowIndex, false);
8222             var view = this.grid.view ? this.grid.view : this.grid;
8223             view.focusRow(rowIndex);
8224              this.fireEvent("afterselectionchange", this);
8225         }
8226     },
8227     
8228     /**
8229      * Selects multiple rows.
8230      * @param {Array} rows Array of the indexes of the row to select
8231      * @param {Boolean} keepExisting (optional) True to keep existing selections
8232      */
8233     selectRows : function(rows, keepExisting){
8234         if(!keepExisting){
8235             this.clearSelections();
8236         }
8237         for(var i = 0, len = rows.length; i < len; i++){
8238             this.selectRow(rows[i], true);
8239         }
8240     },
8241
8242     /**
8243      * Selects a range of rows. All rows in between startRow and endRow are also selected.
8244      * @param {Number} startRow The index of the first row in the range
8245      * @param {Number} endRow The index of the last row in the range
8246      * @param {Boolean} keepExisting (optional) True to retain existing selections
8247      */
8248     selectRange : function(startRow, endRow, keepExisting){
8249         if(this.locked) {
8250             return;
8251         }
8252         if(!keepExisting){
8253             this.clearSelections();
8254         }
8255         if(startRow <= endRow){
8256             for(var i = startRow; i <= endRow; i++){
8257                 this.selectRow(i, true);
8258             }
8259         }else{
8260             for(var i = startRow; i >= endRow; i--){
8261                 this.selectRow(i, true);
8262             }
8263         }
8264     },
8265
8266     /**
8267      * Deselects a range of rows. All rows in between startRow and endRow are also deselected.
8268      * @param {Number} startRow The index of the first row in the range
8269      * @param {Number} endRow The index of the last row in the range
8270      */
8271     deselectRange : function(startRow, endRow, preventViewNotify){
8272         if(this.locked) {
8273             return;
8274         }
8275         for(var i = startRow; i <= endRow; i++){
8276             this.deselectRow(i, preventViewNotify);
8277         }
8278     },
8279
8280     /**
8281      * Selects a row.
8282      * @param {Number} row The index of the row to select
8283      * @param {Boolean} keepExisting (optional) True to keep existing selections
8284      */
8285     selectRow : function(index, keepExisting, preventViewNotify){
8286         if(this.locked || (index < 0 || index >= this.grid.ds.getCount())) {
8287             return;
8288         }
8289         if(this.fireEvent("beforerowselect", this, index, keepExisting) !== false){
8290             if(!keepExisting || this.singleSelect){
8291                 this.clearSelections();
8292             }
8293             var r = this.grid.ds.getAt(index);
8294             this.selections.add(r);
8295             this.last = this.lastActive = index;
8296             if(!preventViewNotify){
8297                 var view = this.grid.view ? this.grid.view : this.grid;
8298                 view.onRowSelect(index);
8299             }
8300             this.fireEvent("rowselect", this, index, r);
8301             this.fireEvent("selectionchange", this);
8302         }
8303     },
8304
8305     /**
8306      * Deselects a row.
8307      * @param {Number} row The index of the row to deselect
8308      */
8309     deselectRow : function(index, preventViewNotify){
8310         if(this.locked) {
8311             return;
8312         }
8313         if(this.last == index){
8314             this.last = false;
8315         }
8316         if(this.lastActive == index){
8317             this.lastActive = false;
8318         }
8319         var r = this.grid.ds.getAt(index);
8320         this.selections.remove(r);
8321         if(!preventViewNotify){
8322             var view = this.grid.view ? this.grid.view : this.grid;
8323             view.onRowDeselect(index);
8324         }
8325         this.fireEvent("rowdeselect", this, index);
8326         this.fireEvent("selectionchange", this);
8327     },
8328
8329     // private
8330     restoreLast : function(){
8331         if(this._last){
8332             this.last = this._last;
8333         }
8334     },
8335
8336     // private
8337     acceptsNav : function(row, col, cm){
8338         return !cm.isHidden(col) && cm.isCellEditable(col, row);
8339     },
8340
8341     // private
8342     onEditorKey : function(field, e){
8343         var k = e.getKey(), newCell, g = this.grid, ed = g.activeEditor;
8344         if(k == e.TAB){
8345             e.stopEvent();
8346             ed.completeEdit();
8347             if(e.shiftKey){
8348                 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
8349             }else{
8350                 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
8351             }
8352         }else if(k == e.ENTER && !e.ctrlKey){
8353             e.stopEvent();
8354             ed.completeEdit();
8355             if(e.shiftKey){
8356                 newCell = g.walkCells(ed.row-1, ed.col, -1, this.acceptsNav, this);
8357             }else{
8358                 newCell = g.walkCells(ed.row+1, ed.col, 1, this.acceptsNav, this);
8359             }
8360         }else if(k == e.ESC){
8361             ed.cancelEdit();
8362         }
8363         if(newCell){
8364             g.startEditing(newCell[0], newCell[1]);
8365         }
8366     }
8367 });/*
8368  * Based on:
8369  * Ext JS Library 1.1.1
8370  * Copyright(c) 2006-2007, Ext JS, LLC.
8371  *
8372  * Originally Released Under LGPL - original licence link has changed is not relivant.
8373  *
8374  * Fork - LGPL
8375  * <script type="text/javascript">
8376  */
8377  
8378
8379 /**
8380  * @class Roo.grid.ColumnModel
8381  * @extends Roo.util.Observable
8382  * This is the default implementation of a ColumnModel used by the Grid. It defines
8383  * the columns in the grid.
8384  * <br>Usage:<br>
8385  <pre><code>
8386  var colModel = new Roo.grid.ColumnModel([
8387         {header: "Ticker", width: 60, sortable: true, locked: true},
8388         {header: "Company Name", width: 150, sortable: true},
8389         {header: "Market Cap.", width: 100, sortable: true},
8390         {header: "$ Sales", width: 100, sortable: true, renderer: money},
8391         {header: "Employees", width: 100, sortable: true, resizable: false}
8392  ]);
8393  </code></pre>
8394  * <p>
8395  
8396  * The config options listed for this class are options which may appear in each
8397  * individual column definition.
8398  * <br/>RooJS Fix - column id's are not sequential but use Roo.id() - fixes bugs with layouts.
8399  * @constructor
8400  * @param {Object} config An Array of column config objects. See this class's
8401  * config objects for details.
8402 */
8403 Roo.grid.ColumnModel = function(config){
8404         /**
8405      * The config passed into the constructor
8406      */
8407     this.config = []; //config;
8408     this.lookup = {};
8409
8410     // if no id, create one
8411     // if the column does not have a dataIndex mapping,
8412     // map it to the order it is in the config
8413     for(var i = 0, len = config.length; i < len; i++){
8414         this.addColumn(config[i]);
8415         
8416     }
8417
8418     /**
8419      * The width of columns which have no width specified (defaults to 100)
8420      * @type Number
8421      */
8422     this.defaultWidth = 100;
8423
8424     /**
8425      * Default sortable of columns which have no sortable specified (defaults to false)
8426      * @type Boolean
8427      */
8428     this.defaultSortable = false;
8429
8430     this.addEvents({
8431         /**
8432              * @event widthchange
8433              * Fires when the width of a column changes.
8434              * @param {ColumnModel} this
8435              * @param {Number} columnIndex The column index
8436              * @param {Number} newWidth The new width
8437              */
8438             "widthchange": true,
8439         /**
8440              * @event headerchange
8441              * Fires when the text of a header changes.
8442              * @param {ColumnModel} this
8443              * @param {Number} columnIndex The column index
8444              * @param {Number} newText The new header text
8445              */
8446             "headerchange": true,
8447         /**
8448              * @event hiddenchange
8449              * Fires when a column is hidden or "unhidden".
8450              * @param {ColumnModel} this
8451              * @param {Number} columnIndex The column index
8452              * @param {Boolean} hidden true if hidden, false otherwise
8453              */
8454             "hiddenchange": true,
8455             /**
8456          * @event columnmoved
8457          * Fires when a column is moved.
8458          * @param {ColumnModel} this
8459          * @param {Number} oldIndex
8460          * @param {Number} newIndex
8461          */
8462         "columnmoved" : true,
8463         /**
8464          * @event columlockchange
8465          * Fires when a column's locked state is changed
8466          * @param {ColumnModel} this
8467          * @param {Number} colIndex
8468          * @param {Boolean} locked true if locked
8469          */
8470         "columnlockchange" : true
8471     });
8472     Roo.grid.ColumnModel.superclass.constructor.call(this);
8473 };
8474 Roo.extend(Roo.grid.ColumnModel, Roo.util.Observable, {
8475     /**
8476      * @cfg {String} header [required] The header text to display in the Grid view.
8477      */
8478         /**
8479      * @cfg {String} xsHeader Header at Bootsrap Extra Small width (default for all)
8480      */
8481         /**
8482      * @cfg {String} smHeader Header at Bootsrap Small width
8483      */
8484         /**
8485      * @cfg {String} mdHeader Header at Bootsrap Medium width
8486      */
8487         /**
8488      * @cfg {String} lgHeader Header at Bootsrap Large width
8489      */
8490         /**
8491      * @cfg {String} xlHeader Header at Bootsrap extra Large width
8492      */
8493     /**
8494      * @cfg {String} dataIndex  The name of the field in the grid's {@link Roo.data.Store}'s
8495      * {@link Roo.data.Record} definition from which to draw the column's value. If not
8496      * specified, the column's index is used as an index into the Record's data Array.
8497      */
8498     /**
8499      * @cfg {Number} width  The initial width in pixels of the column. Using this
8500      * instead of {@link Roo.grid.Grid#autoSizeColumns} is more efficient.
8501      */
8502     /**
8503      * @cfg {Boolean} sortable True if sorting is to be allowed on this column.
8504      * Defaults to the value of the {@link #defaultSortable} property.
8505      * Whether local/remote sorting is used is specified in {@link Roo.data.Store#remoteSort}.
8506      */
8507     /**
8508      * @cfg {Boolean} locked  True to lock the column in place while scrolling the Grid.  Defaults to false.
8509      */
8510     /**
8511      * @cfg {Boolean} fixed  True if the column width cannot be changed.  Defaults to false.
8512      */
8513     /**
8514      * @cfg {Boolean} resizable  False to disable column resizing. Defaults to true.
8515      */
8516     /**
8517      * @cfg {Boolean} hidden  True to hide the column. Defaults to false.
8518      */
8519     /**
8520      * @cfg {Function} renderer A function used to generate HTML markup for a cell
8521      * given the cell's data value. See {@link #setRenderer}. If not specified, the
8522      * default renderer returns the escaped data value. If an object is returned (bootstrap only)
8523      * then it is treated as a Roo Component object instance, and it is rendered after the initial row is rendered
8524      */
8525        /**
8526      * @cfg {Roo.grid.GridEditor} editor  For grid editors - returns the grid editor 
8527      */
8528     /**
8529      * @cfg {String} align (left|right) Set the CSS text-align property of the column.  Defaults to undefined (left).
8530      */
8531     /**
8532      * @cfg {String} valign (top|bottom|middle) Set the CSS vertical-align property of the column (eg. middle, top, bottom etc).  Defaults to undefined (middle)
8533      */
8534     /**
8535      * @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)
8536      */
8537     /**
8538      * @cfg {String} tooltip mouse over tooltip text
8539      */
8540     /**
8541      * @cfg {Number} xs  can be '0' for hidden at this size (number less than 12)
8542      */
8543     /**
8544      * @cfg {Number} sm can be '0' for hidden at this size (number less than 12)
8545      */
8546     /**
8547      * @cfg {Number} md can be '0' for hidden at this size (number less than 12)
8548      */
8549     /**
8550      * @cfg {Number} lg   can be '0' for hidden at this size (number less than 12)
8551      */
8552         /**
8553      * @cfg {Number} xl   can be '0' for hidden at this size (number less than 12)
8554      */
8555     /**
8556      * Returns the id of the column at the specified index.
8557      * @param {Number} index The column index
8558      * @return {String} the id
8559      */
8560     getColumnId : function(index){
8561         return this.config[index].id;
8562     },
8563
8564     /**
8565      * Returns the column for a specified id.
8566      * @param {String} id The column id
8567      * @return {Object} the column
8568      */
8569     getColumnById : function(id){
8570         return this.lookup[id];
8571     },
8572
8573     
8574     /**
8575      * Returns the column Object for a specified dataIndex.
8576      * @param {String} dataIndex The column dataIndex
8577      * @return {Object|Boolean} the column or false if not found
8578      */
8579     getColumnByDataIndex: function(dataIndex){
8580         var index = this.findColumnIndex(dataIndex);
8581         return index > -1 ? this.config[index] : false;
8582     },
8583     
8584     /**
8585      * Returns the index for a specified column id.
8586      * @param {String} id The column id
8587      * @return {Number} the index, or -1 if not found
8588      */
8589     getIndexById : function(id){
8590         for(var i = 0, len = this.config.length; i < len; i++){
8591             if(this.config[i].id == id){
8592                 return i;
8593             }
8594         }
8595         return -1;
8596     },
8597     
8598     /**
8599      * Returns the index for a specified column dataIndex.
8600      * @param {String} dataIndex The column dataIndex
8601      * @return {Number} the index, or -1 if not found
8602      */
8603     
8604     findColumnIndex : function(dataIndex){
8605         for(var i = 0, len = this.config.length; i < len; i++){
8606             if(this.config[i].dataIndex == dataIndex){
8607                 return i;
8608             }
8609         }
8610         return -1;
8611     },
8612     
8613     
8614     moveColumn : function(oldIndex, newIndex){
8615         var c = this.config[oldIndex];
8616         this.config.splice(oldIndex, 1);
8617         this.config.splice(newIndex, 0, c);
8618         this.dataMap = null;
8619         this.fireEvent("columnmoved", this, oldIndex, newIndex);
8620     },
8621
8622     isLocked : function(colIndex){
8623         return this.config[colIndex].locked === true;
8624     },
8625
8626     setLocked : function(colIndex, value, suppressEvent){
8627         if(this.isLocked(colIndex) == value){
8628             return;
8629         }
8630         this.config[colIndex].locked = value;
8631         if(!suppressEvent){
8632             this.fireEvent("columnlockchange", this, colIndex, value);
8633         }
8634     },
8635
8636     getTotalLockedWidth : function(){
8637         var totalWidth = 0;
8638         for(var i = 0; i < this.config.length; i++){
8639             if(this.isLocked(i) && !this.isHidden(i)){
8640                 this.totalWidth += this.getColumnWidth(i);
8641             }
8642         }
8643         return totalWidth;
8644     },
8645
8646     getLockedCount : function(){
8647         for(var i = 0, len = this.config.length; i < len; i++){
8648             if(!this.isLocked(i)){
8649                 return i;
8650             }
8651         }
8652         
8653         return this.config.length;
8654     },
8655
8656     /**
8657      * Returns the number of columns.
8658      * @return {Number}
8659      */
8660     getColumnCount : function(visibleOnly){
8661         if(visibleOnly === true){
8662             var c = 0;
8663             for(var i = 0, len = this.config.length; i < len; i++){
8664                 if(!this.isHidden(i)){
8665                     c++;
8666                 }
8667             }
8668             return c;
8669         }
8670         return this.config.length;
8671     },
8672
8673     /**
8674      * Returns the column configs that return true by the passed function that is called with (columnConfig, index)
8675      * @param {Function} fn
8676      * @param {Object} scope (optional)
8677      * @return {Array} result
8678      */
8679     getColumnsBy : function(fn, scope){
8680         var r = [];
8681         for(var i = 0, len = this.config.length; i < len; i++){
8682             var c = this.config[i];
8683             if(fn.call(scope||this, c, i) === true){
8684                 r[r.length] = c;
8685             }
8686         }
8687         return r;
8688     },
8689
8690     /**
8691      * Returns true if the specified column is sortable.
8692      * @param {Number} col The column index
8693      * @return {Boolean}
8694      */
8695     isSortable : function(col){
8696         if(typeof this.config[col].sortable == "undefined"){
8697             return this.defaultSortable;
8698         }
8699         return this.config[col].sortable;
8700     },
8701
8702     /**
8703      * Returns the rendering (formatting) function defined for the column.
8704      * @param {Number} col The column index.
8705      * @return {Function} The function used to render the cell. See {@link #setRenderer}.
8706      */
8707     getRenderer : function(col){
8708         if(!this.config[col].renderer){
8709             return Roo.grid.ColumnModel.defaultRenderer;
8710         }
8711         return this.config[col].renderer;
8712     },
8713
8714     /**
8715      * Sets the rendering (formatting) function for a column.
8716      * @param {Number} col The column index
8717      * @param {Function} fn The function to use to process the cell's raw data
8718      * to return HTML markup for the grid view. The render function is called with
8719      * the following parameters:<ul>
8720      * <li>Data value.</li>
8721      * <li>Cell metadata. An object in which you may set the following attributes:<ul>
8722      * <li>css A CSS style string to apply to the table cell.</li>
8723      * <li>attr An HTML attribute definition string to apply to the data container element <i>within</i> the table cell.</li></ul>
8724      * <li>The {@link Roo.data.Record} from which the data was extracted.</li>
8725      * <li>Row index</li>
8726      * <li>Column index</li>
8727      * <li>The {@link Roo.data.Store} object from which the Record was extracted</li></ul>
8728      */
8729     setRenderer : function(col, fn){
8730         this.config[col].renderer = fn;
8731     },
8732
8733     /**
8734      * Returns the width for the specified column.
8735      * @param {Number} col The column index
8736      * @param (optional) {String} gridSize bootstrap width size.
8737      * @return {Number}
8738      */
8739     getColumnWidth : function(col, gridSize)
8740         {
8741                 var cfg = this.config[col];
8742                 
8743                 if (typeof(gridSize) == 'undefined') {
8744                         return cfg.width * 1 || this.defaultWidth;
8745                 }
8746                 if (gridSize === false) { // if we set it..
8747                         return cfg.width || false;
8748                 }
8749                 var sizes = ['xl', 'lg', 'md', 'sm', 'xs'];
8750                 
8751                 for(var i = sizes.indexOf(gridSize); i < sizes.length; i++) {
8752                         if (typeof(cfg[ sizes[i] ] ) == 'undefined') {
8753                                 continue;
8754                         }
8755                         return cfg[ sizes[i] ];
8756                 }
8757                 return 1;
8758                 
8759     },
8760
8761     /**
8762      * Sets the width for a column.
8763      * @param {Number} col The column index
8764      * @param {Number} width The new width
8765      */
8766     setColumnWidth : function(col, width, suppressEvent){
8767         this.config[col].width = width;
8768         this.totalWidth = null;
8769         if(!suppressEvent){
8770              this.fireEvent("widthchange", this, col, width);
8771         }
8772     },
8773
8774     /**
8775      * Returns the total width of all columns.
8776      * @param {Boolean} includeHidden True to include hidden column widths
8777      * @return {Number}
8778      */
8779     getTotalWidth : function(includeHidden){
8780         if(!this.totalWidth){
8781             this.totalWidth = 0;
8782             for(var i = 0, len = this.config.length; i < len; i++){
8783                 if(includeHidden || !this.isHidden(i)){
8784                     this.totalWidth += this.getColumnWidth(i);
8785                 }
8786             }
8787         }
8788         return this.totalWidth;
8789     },
8790
8791     /**
8792      * Returns the header for the specified column.
8793      * @param {Number} col The column index
8794      * @return {String}
8795      */
8796     getColumnHeader : function(col){
8797         return this.config[col].header;
8798     },
8799
8800     /**
8801      * Sets the header for a column.
8802      * @param {Number} col The column index
8803      * @param {String} header The new header
8804      */
8805     setColumnHeader : function(col, header){
8806         this.config[col].header = header;
8807         this.fireEvent("headerchange", this, col, header);
8808     },
8809
8810     /**
8811      * Returns the tooltip for the specified column.
8812      * @param {Number} col The column index
8813      * @return {String}
8814      */
8815     getColumnTooltip : function(col){
8816             return this.config[col].tooltip;
8817     },
8818     /**
8819      * Sets the tooltip for a column.
8820      * @param {Number} col The column index
8821      * @param {String} tooltip The new tooltip
8822      */
8823     setColumnTooltip : function(col, tooltip){
8824             this.config[col].tooltip = tooltip;
8825     },
8826
8827     /**
8828      * Returns the dataIndex for the specified column.
8829      * @param {Number} col The column index
8830      * @return {Number}
8831      */
8832     getDataIndex : function(col){
8833         return this.config[col].dataIndex;
8834     },
8835
8836     /**
8837      * Sets the dataIndex for a column.
8838      * @param {Number} col The column index
8839      * @param {Number} dataIndex The new dataIndex
8840      */
8841     setDataIndex : function(col, dataIndex){
8842         this.config[col].dataIndex = dataIndex;
8843     },
8844
8845     
8846     
8847     /**
8848      * Returns true if the cell is editable.
8849      * @param {Number} colIndex The column index
8850      * @param {Number} rowIndex The row index - this is nto actually used..?
8851      * @return {Boolean}
8852      */
8853     isCellEditable : function(colIndex, rowIndex){
8854         return (this.config[colIndex].editable || (typeof this.config[colIndex].editable == "undefined" && this.config[colIndex].editor)) ? true : false;
8855     },
8856
8857     /**
8858      * Returns the editor defined for the cell/column.
8859      * return false or null to disable editing.
8860      * @param {Number} colIndex The column index
8861      * @param {Number} rowIndex The row index
8862      * @return {Object}
8863      */
8864     getCellEditor : function(colIndex, rowIndex){
8865         return this.config[colIndex].editor;
8866     },
8867
8868     /**
8869      * Sets if a column is editable.
8870      * @param {Number} col The column index
8871      * @param {Boolean} editable True if the column is editable
8872      */
8873     setEditable : function(col, editable){
8874         this.config[col].editable = editable;
8875     },
8876
8877
8878     /**
8879      * Returns true if the column is hidden.
8880      * @param {Number} colIndex The column index
8881      * @return {Boolean}
8882      */
8883     isHidden : function(colIndex){
8884         return this.config[colIndex].hidden;
8885     },
8886
8887
8888     /**
8889      * Returns true if the column width cannot be changed
8890      */
8891     isFixed : function(colIndex){
8892         return this.config[colIndex].fixed;
8893     },
8894
8895     /**
8896      * Returns true if the column can be resized
8897      * @return {Boolean}
8898      */
8899     isResizable : function(colIndex){
8900         return colIndex >= 0 && this.config[colIndex].resizable !== false && this.config[colIndex].fixed !== true;
8901     },
8902     /**
8903      * Sets if a column is hidden.
8904      * @param {Number} colIndex The column index
8905      * @param {Boolean} hidden True if the column is hidden
8906      */
8907     setHidden : function(colIndex, hidden){
8908         this.config[colIndex].hidden = hidden;
8909         this.totalWidth = null;
8910         this.fireEvent("hiddenchange", this, colIndex, hidden);
8911     },
8912
8913     /**
8914      * Sets the editor for a column.
8915      * @param {Number} col The column index
8916      * @param {Object} editor The editor object
8917      */
8918     setEditor : function(col, editor){
8919         this.config[col].editor = editor;
8920     },
8921     /**
8922      * Add a column (experimental...) - defaults to adding to the end..
8923      * @param {Object} config 
8924     */
8925     addColumn : function(c)
8926     {
8927     
8928         var i = this.config.length;
8929         this.config[i] = c;
8930         
8931         if(typeof c.dataIndex == "undefined"){
8932             c.dataIndex = i;
8933         }
8934         if(typeof c.renderer == "string"){
8935             c.renderer = Roo.util.Format[c.renderer];
8936         }
8937         if(typeof c.id == "undefined"){
8938             c.id = Roo.id();
8939         }
8940         if(c.editor && c.editor.xtype){
8941             c.editor  = Roo.factory(c.editor, Roo.grid);
8942         }
8943         if(c.editor && c.editor.isFormField){
8944             c.editor = new Roo.grid.GridEditor(c.editor);
8945         }
8946         this.lookup[c.id] = c;
8947     }
8948     
8949 });
8950
8951 Roo.grid.ColumnModel.defaultRenderer = function(value)
8952 {
8953     if(typeof value == "object") {
8954         return value;
8955     }
8956         if(typeof value == "string" && value.length < 1){
8957             return "&#160;";
8958         }
8959     
8960         return String.format("{0}", value);
8961 };
8962
8963 // Alias for backwards compatibility
8964 Roo.grid.DefaultColumnModel = Roo.grid.ColumnModel;
8965 /*
8966  * Based on:
8967  * Ext JS Library 1.1.1
8968  * Copyright(c) 2006-2007, Ext JS, LLC.
8969  *
8970  * Originally Released Under LGPL - original licence link has changed is not relivant.
8971  *
8972  * Fork - LGPL
8973  * <script type="text/javascript">
8974  */
8975  
8976 /**
8977  * @class Roo.LoadMask
8978  * A simple utility class for generically masking elements while loading data.  If the element being masked has
8979  * an underlying {@link Roo.data.Store}, the masking will be automatically synchronized with the store's loading
8980  * process and the mask element will be cached for reuse.  For all other elements, this mask will replace the
8981  * element's UpdateManager load indicator and will be destroyed after the initial load.
8982  * @constructor
8983  * Create a new LoadMask
8984  * @param {String/HTMLElement/Roo.Element} el The element or DOM node, or its id
8985  * @param {Object} config The config object
8986  */
8987 Roo.LoadMask = function(el, config){
8988     this.el = Roo.get(el);
8989     Roo.apply(this, config);
8990     if(this.store){
8991         this.store.on('beforeload', this.onBeforeLoad, this);
8992         this.store.on('load', this.onLoad, this);
8993         this.store.on('loadexception', this.onLoadException, this);
8994         this.removeMask = false;
8995     }else{
8996         var um = this.el.getUpdateManager();
8997         um.showLoadIndicator = false; // disable the default indicator
8998         um.on('beforeupdate', this.onBeforeLoad, this);
8999         um.on('update', this.onLoad, this);
9000         um.on('failure', this.onLoad, this);
9001         this.removeMask = true;
9002     }
9003 };
9004
9005 Roo.LoadMask.prototype = {
9006     /**
9007      * @cfg {Boolean} removeMask
9008      * True to create a single-use mask that is automatically destroyed after loading (useful for page loads),
9009      * False to persist the mask element reference for multiple uses (e.g., for paged data widgets).  Defaults to false.
9010      */
9011     removeMask : false,
9012     /**
9013      * @cfg {String} msg
9014      * The text to display in a centered loading message box (defaults to 'Loading...')
9015      */
9016     msg : 'Loading...',
9017     /**
9018      * @cfg {String} msgCls
9019      * The CSS class to apply to the loading message element (defaults to "x-mask-loading")
9020      */
9021     msgCls : 'x-mask-loading',
9022
9023     /**
9024      * Read-only. True if the mask is currently disabled so that it will not be displayed (defaults to false)
9025      * @type Boolean
9026      */
9027     disabled: false,
9028
9029     /**
9030      * Disables the mask to prevent it from being displayed
9031      */
9032     disable : function(){
9033        this.disabled = true;
9034     },
9035
9036     /**
9037      * Enables the mask so that it can be displayed
9038      */
9039     enable : function(){
9040         this.disabled = false;
9041     },
9042     
9043     onLoadException : function()
9044     {
9045         Roo.log(arguments);
9046         
9047         if (typeof(arguments[3]) != 'undefined') {
9048             Roo.MessageBox.alert("Error loading",arguments[3]);
9049         } 
9050         /*
9051         try {
9052             if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
9053                 Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
9054             }   
9055         } catch(e) {
9056             
9057         }
9058         */
9059     
9060         (function() { this.el.unmask(this.removeMask); }).defer(50, this);
9061     },
9062     // private
9063     onLoad : function()
9064     {
9065         (function() { this.el.unmask(this.removeMask); }).defer(50, this);
9066     },
9067
9068     // private
9069     onBeforeLoad : function(){
9070         if(!this.disabled){
9071             (function() { this.el.mask(this.msg, this.msgCls); }).defer(50, this);
9072         }
9073     },
9074
9075     // private
9076     destroy : function(){
9077         if(this.store){
9078             this.store.un('beforeload', this.onBeforeLoad, this);
9079             this.store.un('load', this.onLoad, this);
9080             this.store.un('loadexception', this.onLoadException, this);
9081         }else{
9082             var um = this.el.getUpdateManager();
9083             um.un('beforeupdate', this.onBeforeLoad, this);
9084             um.un('update', this.onLoad, this);
9085             um.un('failure', this.onLoad, this);
9086         }
9087     }
9088 };/**
9089  * @class Roo.bootstrap.Table
9090  * @licence LGBL
9091  * @extends Roo.bootstrap.Component
9092  * @children Roo.bootstrap.TableBody
9093  * Bootstrap Table class.  This class represents the primary interface of a component based grid control.
9094  * Similar to Roo.grid.Grid
9095  * <pre><code>
9096  var table = Roo.factory({
9097     xtype : 'Table',
9098     xns : Roo.bootstrap,
9099     autoSizeColumns: true,
9100     
9101     
9102     store : {
9103         xtype : 'Store',
9104         xns : Roo.data,
9105         remoteSort : true,
9106         sortInfo : { direction : 'ASC', field: 'name' },
9107         proxy : {
9108            xtype : 'HttpProxy',
9109            xns : Roo.data,
9110            method : 'GET',
9111            url : 'https://example.com/some.data.url.json'
9112         },
9113         reader : {
9114            xtype : 'JsonReader',
9115            xns : Roo.data,
9116            fields : [ 'id', 'name', whatever' ],
9117            id : 'id',
9118            root : 'data'
9119         }
9120     },
9121     cm : [
9122         {
9123             xtype : 'ColumnModel',
9124             xns : Roo.grid,
9125             align : 'center',
9126             cursor : 'pointer',
9127             dataIndex : 'is_in_group',
9128             header : "Name",
9129             sortable : true,
9130             renderer : function(v, x , r) {  
9131             
9132                 return String.format("{0}", v)
9133             }
9134             width : 3
9135         } // more columns..
9136     ],
9137     selModel : {
9138         xtype : 'RowSelectionModel',
9139         xns : Roo.bootstrap.Table
9140         // you can add listeners to catch selection change here....
9141     }
9142      
9143
9144  });
9145  // set any options
9146  grid.render(Roo.get("some-div"));
9147 </code></pre>
9148
9149 Currently the Table  uses multiple headers to try and handle XL / Medium etc... styling
9150
9151
9152
9153  *
9154  * @cfg {Roo.grid.AbstractSelectionModel} sm The selection model to use (cell selection is not supported yet)
9155  * @cfg {Roo.data.Store} store The data store to use
9156  * @cfg {Roo.grid.ColumnModel} cm[] A column for the grid.
9157  * 
9158  * @cfg {String} cls table class
9159  *
9160  *
9161  * @cfg {string} empty_results  Text to display for no results 
9162  * @cfg {boolean} striped Should the rows be alternative striped
9163  * @cfg {boolean} bordered Add borders to the table
9164  * @cfg {boolean} hover Add hover highlighting
9165  * @cfg {boolean} condensed Format condensed
9166  * @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,
9167  *                also adds table-responsive (see bootstrap docs for details)
9168  * @cfg {Boolean} loadMask (true|false) default false
9169  * @cfg {Boolean} footerShow (true|false) generate tfoot, default true
9170  * @cfg {Boolean} footerRow (true|false) generate tfoot with columns of values, default false
9171  * @cfg {Boolean} headerShow (true|false) generate thead, default true
9172  * @cfg {Boolean} rowSelection (true|false) default false
9173  * @cfg {Boolean} cellSelection (true|false) default false
9174  * @cfg {Boolean} scrollBody (true|false) default false - body scrolled / fixed header (with resizable columns)
9175  * @cfg {Roo.bootstrap.PagingToolbar} footer  a paging toolbar
9176  * @cfg {Boolean} lazyLoad  auto load data while scrolling to the end (default false)
9177  * @cfg {Boolean} auto_hide_footer  auto hide footer if only one page (default false)
9178  * @cfg {Boolean} enableColumnResize default true if columns can be resized = needs scrollBody to be set to work (drag/drop)
9179  * @cfg {Boolean} disableAutoSize disable autoSize() and initCSS()
9180  *
9181  * 
9182  * @cfg {Number} minColumnWidth default 50 pixels minimum column width 
9183  * 
9184  * @constructor
9185  * Create a new Table
9186  * @param {Object} config The config object
9187  */
9188
9189 Roo.bootstrap.Table = function(config)
9190 {
9191     Roo.bootstrap.Table.superclass.constructor.call(this, config);
9192      
9193     // BC...
9194     this.rowSelection = (typeof(config.rowSelection) != 'undefined') ? config.rowSelection : this.rowSelection;
9195     this.cellSelection = (typeof(config.cellSelection) != 'undefined') ? config.cellSelection : this.cellSelection;
9196     this.headerShow = (typeof(config.thead) != 'undefined') ? config.thead : this.headerShow;
9197     this.footerShow = (typeof(config.tfoot) != 'undefined') ? config.tfoot : this.footerShow;
9198     
9199     this.view = this; // compat with grid.
9200     
9201     this.sm = this.sm || {xtype: 'RowSelectionModel'};
9202     if (this.sm) {
9203         this.sm.grid = this;
9204         this.selModel = Roo.factory(this.sm, Roo.grid);
9205         this.sm = this.selModel;
9206         this.sm.xmodule = this.xmodule || false;
9207     }
9208     
9209     if (this.cm && typeof(this.cm.config) == 'undefined') {
9210         this.colModel = new Roo.grid.ColumnModel(this.cm);
9211         this.cm = this.colModel;
9212         this.cm.xmodule = this.xmodule || false;
9213     }
9214     if (this.store) {
9215         this.store= Roo.factory(this.store, Roo.data);
9216         this.ds = this.store;
9217         this.ds.xmodule = this.xmodule || false;
9218          
9219     }
9220     if (this.footer && this.store) {
9221         this.footer.dataSource = this.ds;
9222         this.footer = Roo.factory(this.footer);
9223     }
9224     
9225     /** @private */
9226     this.addEvents({
9227         /**
9228          * @event cellclick
9229          * Fires when a cell is clicked
9230          * @param {Roo.bootstrap.Table} this
9231          * @param {Roo.Element} el
9232          * @param {Number} rowIndex
9233          * @param {Number} columnIndex
9234          * @param {Roo.EventObject} e
9235          */
9236         "cellclick" : true,
9237         /**
9238          * @event celldblclick
9239          * Fires when a cell is double clicked
9240          * @param {Roo.bootstrap.Table} this
9241          * @param {Roo.Element} el
9242          * @param {Number} rowIndex
9243          * @param {Number} columnIndex
9244          * @param {Roo.EventObject} e
9245          */
9246         "celldblclick" : true,
9247         /**
9248          * @event rowclick
9249          * Fires when a row is clicked
9250          * @param {Roo.bootstrap.Table} this
9251          * @param {Roo.Element} el
9252          * @param {Number} rowIndex
9253          * @param {Roo.EventObject} e
9254          */
9255         "rowclick" : true,
9256         /**
9257          * @event rowdblclick
9258          * Fires when a row is double clicked
9259          * @param {Roo.bootstrap.Table} this
9260          * @param {Roo.Element} el
9261          * @param {Number} rowIndex
9262          * @param {Roo.EventObject} e
9263          */
9264         "rowdblclick" : true,
9265         /**
9266          * @event mouseover
9267          * Fires when a mouseover occur
9268          * @param {Roo.bootstrap.Table} this
9269          * @param {Roo.Element} el
9270          * @param {Number} rowIndex
9271          * @param {Number} columnIndex
9272          * @param {Roo.EventObject} e
9273          */
9274         "mouseover" : true,
9275         /**
9276          * @event mouseout
9277          * Fires when a mouseout occur
9278          * @param {Roo.bootstrap.Table} this
9279          * @param {Roo.Element} el
9280          * @param {Number} rowIndex
9281          * @param {Number} columnIndex
9282          * @param {Roo.EventObject} e
9283          */
9284         "mouseout" : true,
9285         /**
9286          * @event rowclass
9287          * Fires when a row is rendered, so you can change add a style to it.
9288          * @param {Roo.bootstrap.Table} this
9289          * @param {Object} rowcfg   contains record  rowIndex colIndex and rowClass - set rowClass to add a style.
9290          */
9291         'rowclass' : true,
9292           /**
9293          * @event rowsrendered
9294          * Fires when all the  rows have been rendered
9295          * @param {Roo.bootstrap.Table} this
9296          */
9297         'rowsrendered' : true,
9298         /**
9299          * @event contextmenu
9300          * The raw contextmenu event for the entire grid.
9301          * @param {Roo.EventObject} e
9302          */
9303         "contextmenu" : true,
9304         /**
9305          * @event rowcontextmenu
9306          * Fires when a row is right clicked
9307          * @param {Roo.bootstrap.Table} this
9308          * @param {Number} rowIndex
9309          * @param {Roo.EventObject} e
9310          */
9311         "rowcontextmenu" : true,
9312         /**
9313          * @event cellcontextmenu
9314          * Fires when a cell is right clicked
9315          * @param {Roo.bootstrap.Table} this
9316          * @param {Number} rowIndex
9317          * @param {Number} cellIndex
9318          * @param {Roo.EventObject} e
9319          */
9320          "cellcontextmenu" : true,
9321          /**
9322          * @event headercontextmenu
9323          * Fires when a header is right clicked
9324          * @param {Roo.bootstrap.Table} this
9325          * @param {Number} columnIndex
9326          * @param {Roo.EventObject} e
9327          */
9328         "headercontextmenu" : true,
9329         /**
9330          * @event mousedown
9331          * The raw mousedown event for the entire grid.
9332          * @param {Roo.EventObject} e
9333          */
9334         "mousedown" : true
9335         
9336     });
9337 };
9338
9339 Roo.extend(Roo.bootstrap.Table, Roo.bootstrap.Component,  {
9340     
9341     cls: false,
9342     
9343     empty_results : '',
9344     striped : false,
9345     scrollBody : false,
9346     bordered: false,
9347     hover:  false,
9348     condensed : false,
9349     responsive : false,
9350     sm : false,
9351     cm : false,
9352     store : false,
9353     loadMask : false,
9354     footerShow : true,
9355     footerRow : false,
9356     headerShow : true,
9357     enableColumnResize: true,
9358     disableAutoSize: false,
9359   
9360     rowSelection : false,
9361     cellSelection : false,
9362     layout : false,
9363
9364     minColumnWidth : 50,
9365     
9366     // Roo.Element - the tbody
9367     bodyEl: false,  // <tbody> Roo.Element - thead element    
9368     headEl: false,  // <thead> Roo.Element - thead element
9369     resizeProxy : false, // proxy element for dragging?
9370
9371
9372     
9373     container: false, // used by gridpanel...
9374     
9375     lazyLoad : false,
9376     
9377     CSS : Roo.util.CSS,
9378     
9379     auto_hide_footer : false,
9380     
9381     view: false, // actually points to this..
9382     
9383     getAutoCreate : function()
9384     {
9385         var cfg = Roo.apply({}, Roo.bootstrap.Table.superclass.getAutoCreate.call(this));
9386         
9387         cfg = {
9388             tag: 'table',
9389             cls : 'table', 
9390             cn : []
9391         };
9392         // this get's auto added by panel.Grid
9393         if (this.scrollBody) {
9394             cfg.cls += ' table-body-fixed';
9395         }    
9396         if (this.striped) {
9397             cfg.cls += ' table-striped';
9398         }
9399         
9400         if (this.hover) {
9401             cfg.cls += ' table-hover';
9402         }
9403         if (this.bordered) {
9404             cfg.cls += ' table-bordered';
9405         }
9406         if (this.condensed) {
9407             cfg.cls += ' table-condensed';
9408         }
9409         
9410         if (this.responsive) {
9411             cfg.cls += ' table-responsive';
9412         }
9413         
9414         if (this.cls) {
9415             cfg.cls+=  ' ' +this.cls;
9416         }
9417         
9418         
9419         
9420         if (this.layout) {
9421             cfg.style = (typeof(cfg.style) == 'undefined') ? ('table-layout:' + this.layout + ';') : (cfg.style + ('table-layout:' + this.layout + ';'));
9422         }
9423         
9424         if(this.store || this.cm){
9425             if(this.headerShow){
9426                 cfg.cn.push(this.renderHeader());
9427             }
9428             
9429             cfg.cn.push(this.renderBody());
9430             
9431             if(this.footerShow || this.footerRow){
9432                 cfg.cn.push(this.renderFooter());
9433             }
9434
9435             // where does this come from?
9436             //cfg.cls+=  ' TableGrid';
9437         }
9438         
9439         return { cn : [ cfg ] };
9440     },
9441     
9442     initEvents : function()
9443     {   
9444         if(!this.store || !this.cm){
9445             return;
9446         }
9447         if (this.selModel) {
9448             this.selModel.initEvents();
9449         }
9450         
9451         
9452         //Roo.log('initEvents with ds!!!!');
9453         
9454         this.bodyEl = this.el.select('tbody', true).first();
9455         this.headEl = this.el.select('thead', true).first();
9456         this.mainFoot = this.el.select('tfoot', true).first();
9457         
9458         
9459         
9460         
9461         Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
9462             e.on('click', this.sort, this);
9463         }, this);
9464         
9465         
9466         // why is this done????? = it breaks dialogs??
9467         //this.parent().el.setStyle('position', 'relative');
9468         
9469         
9470         if (this.footer) {
9471             this.footer.parentId = this.id;
9472             this.footer.onRender(this.el.select('tfoot tr td').first(), null);
9473             
9474             if(this.lazyLoad){
9475                 this.el.select('tfoot tr td').first().addClass('hide');
9476             }
9477         } 
9478         
9479         if(this.loadMask) {
9480             this.maskEl = new Roo.LoadMask(this.el, { store : this.ds, msgCls: 'roo-el-mask-msg' });
9481         }
9482         
9483         this.store.on('load', this.onLoad, this);
9484         this.store.on('beforeload', this.onBeforeLoad, this);
9485         this.store.on('update', this.onUpdate, this);
9486         this.store.on('add', this.onAdd, this);
9487         this.store.on("clear", this.clear, this);
9488         
9489         this.el.on("contextmenu", this.onContextMenu, this);
9490         
9491         
9492         this.cm.on("headerchange", this.onHeaderChange, this);
9493         this.cm.on("hiddenchange", this.onHiddenChange, this, arguments);
9494
9495  //?? does bodyEl get replaced on render?
9496         this.bodyEl.on("click", this.onClick, this);
9497         this.bodyEl.on("dblclick", this.onDblClick, this);        
9498         this.bodyEl.on('scroll', this.onBodyScroll, this);
9499
9500         // guessing mainbody will work - this relays usually caught by selmodel at present.
9501         this.relayEvents(this.bodyEl, ["mousedown","mouseup","mouseover","mouseout","keypress"]);
9502   
9503   
9504         this.resizeProxy = Roo.get(document.body).createChild({ cls:"x-grid-resize-proxy", html: '&#160;' });
9505         
9506   
9507         if(this.headEl && this.enableColumnResize !== false && Roo.grid.SplitDragZone){
9508             new Roo.grid.SplitDragZone(this, this.headEl.dom, false); // not sure what 'lockedHd is for this implementation..)
9509         }
9510         
9511         this.initCSS();
9512     },
9513     // Compatibility with grid - we implement all the view features at present.
9514     getView : function()
9515     {
9516         return this;
9517     },
9518     
9519     initCSS : function()
9520     {
9521         if(this.disableAutoSize) {
9522             return;
9523         }
9524         
9525         var cm = this.cm, styles = [];
9526         this.CSS.removeStyleSheet(this.id + '-cssrules');
9527         var headHeight = this.headEl ? this.headEl.dom.clientHeight : 0;
9528         // we can honour xs/sm/md/xl  as widths...
9529         // we first have to decide what widht we are currently at...
9530         var sz = Roo.getGridSize();
9531         
9532         var total = 0;
9533         var last = -1;
9534         var cols = []; // visable cols.
9535         var total_abs = 0;
9536         for(var i = 0, len = cm.getColumnCount(); i < len; i++) {
9537             var w = cm.getColumnWidth(i, false);
9538             if(cm.isHidden(i)){
9539                 cols.push( { rel : false, abs : 0 });
9540                 continue;
9541             }
9542             if (w !== false) {
9543                 cols.push( { rel : false, abs : w });
9544                 total_abs += w;
9545                 last = i; // not really..
9546                 continue;
9547             }
9548             var w = cm.getColumnWidth(i, sz);
9549             if (w > 0) {
9550                 last = i
9551             }
9552             total += w;
9553             cols.push( { rel : w, abs : false });
9554         }
9555         
9556         var avail = this.bodyEl.dom.clientWidth - total_abs;
9557         
9558         var unitWidth = Math.floor(avail / total);
9559         var rem = avail - (unitWidth * total);
9560         
9561         var hidden, width, pos = 0 , splithide , left;
9562         for(var i = 0, len = cm.getColumnCount(); i < len; i++) {
9563             
9564             hidden = 'display:none;';
9565             left = '';
9566             width  = 'width:0px;';
9567             splithide = '';
9568             if(!cm.isHidden(i)){
9569                 hidden = '';
9570                 
9571                 
9572                 // we can honour xs/sm/md/xl ?
9573                 var w = cols[i].rel == false ? cols[i].abs : (cols[i].rel * unitWidth);
9574                 if (w===0) {
9575                     hidden = 'display:none;';
9576                 }
9577                 // width should return a small number...
9578                 if (i == last) {
9579                     w+=rem; // add the remaining with..
9580                 }
9581                 pos += w;
9582                 left = "left:" + (pos -4) + "px;";
9583                 width = "width:" + w+ "px;";
9584                 
9585             }
9586             if (this.responsive) {
9587                 width = '';
9588                 left = '';
9589                 hidden = cm.isHidden(i) ? 'display:none;' : '';
9590                 splithide = 'display: none;';
9591             }
9592             
9593             styles.push( '#' , this.id , ' .x-col-' , i, " {", cm.config[i].css, width, hidden, "}\n" );
9594             if (this.headEl) {
9595                 if (i == last) {
9596                     splithide = 'display:none;';
9597                 }
9598                 
9599                 styles.push('#' , this.id , ' .x-hcol-' , i, " { ", width, hidden," }\n",
9600                             '#' , this.id , ' .x-grid-split-' , i, " { ", left, splithide, 'height:', (headHeight - 4), "px;}\n",
9601                             // this is the popover version..
9602                             '.popover-inner #' , this.id , ' .x-grid-split-' , i, " { ", left, splithide, 'height:', 100, "%;}\n"
9603                 );
9604             }
9605             
9606         }
9607         //Roo.log(styles.join(''));
9608         this.CSS.createStyleSheet( styles.join(''), this.id + '-cssrules');
9609         
9610     },
9611     
9612     
9613     
9614     onContextMenu : function(e, t)
9615     {
9616         this.processEvent("contextmenu", e);
9617     },
9618     
9619     processEvent : function(name, e)
9620     {
9621         if (name != 'touchstart' ) {
9622             this.fireEvent(name, e);    
9623         }
9624         
9625         var t = e.getTarget();
9626         
9627         var cell = Roo.get(t);
9628         
9629         if(!cell){
9630             return;
9631         }
9632         
9633         if(cell.findParent('tfoot', false, true)){
9634             return;
9635         }
9636         
9637         if(cell.findParent('thead', false, true)){
9638             
9639             if(e.getTarget().nodeName.toLowerCase() != 'th'){
9640                 cell = Roo.get(t).findParent('th', false, true);
9641                 if (!cell) {
9642                     Roo.log("failed to find th in thead?");
9643                     Roo.log(e.getTarget());
9644                     return;
9645                 }
9646             }
9647             
9648             var cellIndex = cell.dom.cellIndex;
9649             
9650             var ename = name == 'touchstart' ? 'click' : name;
9651             this.fireEvent("header" + ename, this, cellIndex, e);
9652             
9653             return;
9654         }
9655         
9656         if(e.getTarget().nodeName.toLowerCase() != 'td'){
9657             cell = Roo.get(t).findParent('td', false, true);
9658             if (!cell) {
9659                 Roo.log("failed to find th in tbody?");
9660                 Roo.log(e.getTarget());
9661                 return;
9662             }
9663         }
9664         
9665         var row = cell.findParent('tr', false, true);
9666         var cellIndex = cell.dom.cellIndex;
9667         var rowIndex = row.dom.rowIndex - 1;
9668         
9669         if(row !== false){
9670             
9671             this.fireEvent("row" + name, this, rowIndex, e);
9672             
9673             if(cell !== false){
9674             
9675                 this.fireEvent("cell" + name, this, rowIndex, cellIndex, e);
9676             }
9677         }
9678         
9679     },
9680     
9681     onMouseover : function(e, el)
9682     {
9683         var cell = Roo.get(el);
9684         
9685         if(!cell){
9686             return;
9687         }
9688         
9689         if(e.getTarget().nodeName.toLowerCase() != 'td'){
9690             cell = cell.findParent('td', false, true);
9691         }
9692         
9693         var row = cell.findParent('tr', false, true);
9694         var cellIndex = cell.dom.cellIndex;
9695         var rowIndex = row.dom.rowIndex - 1; // start from 0
9696         
9697         this.fireEvent('mouseover', this, cell, rowIndex, cellIndex, e);
9698         
9699     },
9700     
9701     onMouseout : function(e, el)
9702     {
9703         var cell = Roo.get(el);
9704         
9705         if(!cell){
9706             return;
9707         }
9708         
9709         if(e.getTarget().nodeName.toLowerCase() != 'td'){
9710             cell = cell.findParent('td', false, true);
9711         }
9712         
9713         var row = cell.findParent('tr', false, true);
9714         var cellIndex = cell.dom.cellIndex;
9715         var rowIndex = row.dom.rowIndex - 1; // start from 0
9716         
9717         this.fireEvent('mouseout', this, cell, rowIndex, cellIndex, e);
9718         
9719     },
9720     
9721     onClick : function(e, el)
9722     {
9723         var cell = Roo.get(el);
9724         
9725         if(!cell || (!this.cellSelection && !this.rowSelection)){
9726             return;
9727         }
9728         
9729         if(e.getTarget().nodeName.toLowerCase() != 'td'){
9730             cell = cell.findParent('td', false, true);
9731         }
9732         
9733         if(!cell || typeof(cell) == 'undefined'){
9734             return;
9735         }
9736         
9737         var row = cell.findParent('tr', false, true);
9738         
9739         if(!row || typeof(row) == 'undefined'){
9740             return;
9741         }
9742         
9743         var cellIndex = cell.dom.cellIndex;
9744         var rowIndex = this.getRowIndex(row);
9745         
9746         // why??? - should these not be based on SelectionModel?
9747         //if(this.cellSelection){
9748             this.fireEvent('cellclick', this, cell, rowIndex, cellIndex, e);
9749         //}
9750         
9751         //if(this.rowSelection){
9752             this.fireEvent('rowclick', this, row, rowIndex, e);
9753         //}
9754          
9755     },
9756         
9757     onDblClick : function(e,el)
9758     {
9759         var cell = Roo.get(el);
9760         
9761         if(!cell || (!this.cellSelection && !this.rowSelection)){
9762             return;
9763         }
9764         
9765         if(e.getTarget().nodeName.toLowerCase() != 'td'){
9766             cell = cell.findParent('td', false, true);
9767         }
9768         
9769         if(!cell || typeof(cell) == 'undefined'){
9770             return;
9771         }
9772         
9773         var row = cell.findParent('tr', false, true);
9774         
9775         if(!row || typeof(row) == 'undefined'){
9776             return;
9777         }
9778         
9779         var cellIndex = cell.dom.cellIndex;
9780         var rowIndex = this.getRowIndex(row);
9781         
9782         if(this.cellSelection){
9783             this.fireEvent('celldblclick', this, cell, rowIndex, cellIndex, e);
9784         }
9785         
9786         if(this.rowSelection){
9787             this.fireEvent('rowdblclick', this, row, rowIndex, e);
9788         }
9789     },
9790     findRowIndex : function(el)
9791     {
9792         var cell = Roo.get(el);
9793         if(!cell) {
9794             return false;
9795         }
9796         var row = cell.findParent('tr', false, true);
9797         
9798         if(!row || typeof(row) == 'undefined'){
9799             return false;
9800         }
9801         return this.getRowIndex(row);
9802     },
9803     sort : function(e,el)
9804     {
9805         var col = Roo.get(el);
9806         
9807         if(!col.hasClass('sortable')){
9808             return;
9809         }
9810         
9811         var sort = col.attr('sort');
9812         var dir = 'ASC';
9813         
9814         if(col.select('i', true).first().hasClass('fa-arrow-up')){
9815             dir = 'DESC';
9816         }
9817         
9818         this.store.sortInfo = {field : sort, direction : dir};
9819         
9820         if (this.footer) {
9821             Roo.log("calling footer first");
9822             this.footer.onClick('first');
9823         } else {
9824         
9825             this.store.load({ params : { start : 0 } });
9826         }
9827     },
9828     
9829     renderHeader : function()
9830     {
9831         var header = {
9832             tag: 'thead',
9833             cn : []
9834         };
9835         
9836         var cm = this.cm;
9837         this.totalWidth = 0;
9838         
9839         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
9840             
9841             var config = cm.config[i];
9842             
9843             var c = {
9844                 tag: 'th',
9845                 cls : 'x-hcol-' + i,
9846                 style : '',
9847                 
9848                 html: cm.getColumnHeader(i)
9849             };
9850             
9851             var tooltip = cm.getColumnTooltip(i);
9852             if (tooltip) {
9853                 c.tooltip = tooltip;
9854             }
9855             
9856             
9857             var hh = '';
9858             
9859             if(typeof(config.sortable) != 'undefined' && config.sortable){
9860                 c.cls += ' sortable';
9861                 c.html = '<i class="fa"></i>' + c.html;
9862             }
9863             
9864             // could use BS4 hidden-..-down 
9865             
9866             if(typeof(config.lgHeader) != 'undefined'){
9867                 hh += '<span class="hidden-xs hidden-sm hidden-md ">' + config.lgHeader + '</span>';
9868             }
9869             
9870             if(typeof(config.mdHeader) != 'undefined'){
9871                 hh += '<span class="hidden-xs hidden-sm hidden-lg">' + config.mdHeader + '</span>';
9872             }
9873             
9874             if(typeof(config.smHeader) != 'undefined'){
9875                 hh += '<span class="hidden-xs hidden-md hidden-lg">' + config.smHeader + '</span>';
9876             }
9877             
9878             if(typeof(config.xsHeader) != 'undefined'){
9879                 hh += '<span class="hidden-sm hidden-md hidden-lg">' + config.xsHeader + '</span>';
9880             }
9881             
9882             if(hh.length){
9883                 c.html = hh;
9884             }
9885             
9886             if(typeof(config.tooltip) != 'undefined'){
9887                 c.tooltip = config.tooltip;
9888             }
9889             
9890             if(typeof(config.colspan) != 'undefined'){
9891                 c.colspan = config.colspan;
9892             }
9893             
9894             // hidden is handled by CSS now
9895             
9896             if(typeof(config.dataIndex) != 'undefined'){
9897                 c.sort = config.dataIndex;
9898             }
9899             
9900            
9901             
9902             if(typeof(config.align) != 'undefined' && config.align.length){
9903                 c.style += ' text-align:' + config.align + ';';
9904             }
9905             
9906             /* width is done in CSS
9907              *if(typeof(config.width) != 'undefined'){
9908                 c.style += ' width:' + config.width + 'px;';
9909                 this.totalWidth += config.width;
9910             } else {
9911                 this.totalWidth += 100; // assume minimum of 100 per column?
9912             }
9913             */
9914             
9915             if(typeof(config.cls) != 'undefined'){
9916                 c.cls = (typeof(c.cls) == 'undefined') ? config.cls : (c.cls + ' ' + config.cls);
9917             }
9918             // this is the bit that doesnt reall work at all...
9919             
9920             if (this.responsive) {
9921                  
9922             
9923                 ['xs','sm','md','lg'].map(function(size){
9924                     
9925                     if(typeof(config[size]) == 'undefined'){
9926                         return;
9927                     }
9928                      
9929                     if (!config[size]) { // 0 = hidden
9930                         // BS 4 '0' is treated as hide that column and below.
9931                         c.cls += ' hidden-' + size + ' hidden' + size + '-down';
9932                         return;
9933                     }
9934                     
9935                     c.cls += ' col-' + size + '-' + config[size] + (
9936                         size == 'xs' ? (' col-' + config[size] ) : '' // bs4 col-{num} replaces col-xs
9937                     );
9938                     
9939                     
9940                 });
9941             }
9942             // at the end?
9943             
9944             c.html +=' <span class="x-grid-split x-grid-split-' + i + '"></span>';
9945             
9946             
9947             
9948             
9949             header.cn.push(c)
9950         }
9951         
9952         return header;
9953     },
9954     
9955     renderBody : function()
9956     {
9957         var body = {
9958             tag: 'tbody',
9959             cn : [
9960                 {
9961                     tag: 'tr',
9962                     cn : [
9963                         {
9964                             tag : 'td',
9965                             colspan :  this.cm.getColumnCount()
9966                         }
9967                     ]
9968                 }
9969             ]
9970         };
9971         
9972         return body;
9973     },
9974     
9975     renderFooter : function()
9976     {
9977         var footer = {
9978             tag: 'tfoot',
9979             cn : [
9980                 {
9981                     tag: 'tr',
9982                     cn : [
9983                         {
9984                             tag : 'td',
9985                             colspan :  this.cm.getColumnCount()
9986                         }
9987                     ]
9988                 }
9989             ]
9990         };
9991         
9992         return footer;
9993     },
9994     
9995     onLoad : function()
9996     {
9997 //        Roo.log('ds onload');
9998         this.clear();
9999         
10000         var _this = this;
10001         var cm = this.cm;
10002         var ds = this.store;
10003         
10004         Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
10005             e.select('i', true).removeClass(['fa-arrow-up', 'fa-arrow-down']);
10006             if (_this.store.sortInfo) {
10007                     
10008                 if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'ASC'){
10009                     e.select('i', true).addClass(['fa-arrow-up']);
10010                 }
10011                 
10012                 if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'DESC'){
10013                     e.select('i', true).addClass(['fa-arrow-down']);
10014                 }
10015             }
10016         });
10017         
10018         var tbody =  this.bodyEl;
10019               
10020         if(ds.getCount() > 0){
10021             ds.data.each(function(d,rowIndex){
10022                 var row =  this.renderRow(cm, ds, rowIndex);
10023                 
10024                 tbody.createChild(row);
10025                 
10026                 var _this = this;
10027                 
10028                 if(row.cellObjects.length){
10029                     Roo.each(row.cellObjects, function(r){
10030                         _this.renderCellObject(r);
10031                     })
10032                 }
10033                 
10034             }, this);
10035         } else if (this.empty_results.length) {
10036             this.el.mask(this.empty_results, 'no-spinner');
10037         }
10038         
10039         var tfoot = this.el.select('tfoot', true).first();
10040         
10041         if(this.footerShow && !this.footerRow && this.auto_hide_footer && this.mainFoot){
10042             
10043             this.mainFoot.setVisibilityMode(Roo.Element.DISPLAY).hide();
10044             
10045             var total = this.ds.getTotalCount();
10046             
10047             if(this.footer.pageSize < total){
10048                 this.mainFoot.show();
10049             }
10050         }
10051
10052         if(!this.footerShow && this.footerRow) {
10053
10054             var tr = {
10055                 tag : 'tr',
10056                 cn : []
10057             };
10058
10059             for(var i = 0, len = cm.getColumnCount(); i < len; i++){
10060                 var footer = typeof(cm.config[i].footer) == "function" ? cm.config[i].footer(ds, cm.config[i]) : cm.config[i].footer;
10061                 var td = {
10062                     tag: 'td',
10063                     cls : ' x-fcol-' + i,
10064                     html: footer
10065                 };
10066
10067                 tr.cn.push(td);
10068                 
10069             }
10070             
10071             tfoot.dom.innerHTML = '';
10072
10073             tfoot.createChild(tr);
10074         }
10075         
10076         Roo.each(this.el.select('tbody td', true).elements, function(e){
10077             e.on('mouseover', _this.onMouseover, _this);
10078         });
10079         
10080         Roo.each(this.el.select('tbody td', true).elements, function(e){
10081             e.on('mouseout', _this.onMouseout, _this);
10082         });
10083         this.fireEvent('rowsrendered', this);
10084         
10085         this.autoSize();
10086         
10087         this.initCSS(); /// resize cols
10088
10089         
10090     },
10091     
10092     
10093     onUpdate : function(ds,record)
10094     {
10095         this.refreshRow(record);
10096         this.autoSize();
10097     },
10098     
10099     onRemove : function(ds, record, index, isUpdate){
10100         if(isUpdate !== true){
10101             this.fireEvent("beforerowremoved", this, index, record);
10102         }
10103         var bt = this.bodyEl.dom;
10104         
10105         var rows = this.el.select('tbody > tr', true).elements;
10106         
10107         if(typeof(rows[index]) != 'undefined'){
10108             bt.removeChild(rows[index].dom);
10109         }
10110         
10111 //        if(bt.rows[index]){
10112 //            bt.removeChild(bt.rows[index]);
10113 //        }
10114         
10115         if(isUpdate !== true){
10116             //this.stripeRows(index);
10117             //this.syncRowHeights(index, index);
10118             //this.layout();
10119             this.fireEvent("rowremoved", this, index, record);
10120         }
10121     },
10122     
10123     onAdd : function(ds, records, rowIndex)
10124     {
10125         //Roo.log('on Add called');
10126         // - note this does not handle multiple adding very well..
10127         var bt = this.bodyEl.dom;
10128         for (var i =0 ; i < records.length;i++) {
10129             //Roo.log('call insert row Add called on ' + rowIndex + ':' + i);
10130             //Roo.log(records[i]);
10131             //Roo.log(this.store.getAt(rowIndex+i));
10132             this.insertRow(this.store, rowIndex + i, false);
10133             return;
10134         }
10135         
10136     },
10137     
10138     
10139     refreshRow : function(record){
10140         var ds = this.store, index;
10141         if(typeof record == 'number'){
10142             index = record;
10143             record = ds.getAt(index);
10144         }else{
10145             index = ds.indexOf(record);
10146             if (index < 0) {
10147                 return; // should not happen - but seems to 
10148             }
10149         }
10150         this.insertRow(ds, index, true);
10151         this.autoSize();
10152         this.onRemove(ds, record, index+1, true);
10153         this.autoSize();
10154         //this.syncRowHeights(index, index);
10155         //this.layout();
10156         this.fireEvent("rowupdated", this, index, record);
10157     },
10158     // private - called by RowSelection
10159     onRowSelect : function(rowIndex){
10160         var row = this.getRowDom(rowIndex);
10161         row.addClass(['bg-info','info']);
10162     },
10163     // private - called by RowSelection
10164     onRowDeselect : function(rowIndex)
10165     {
10166         if (rowIndex < 0) {
10167             return;
10168         }
10169         var row = this.getRowDom(rowIndex);
10170         row.removeClass(['bg-info','info']);
10171     },
10172       /**
10173      * Focuses the specified row.
10174      * @param {Number} row The row index
10175      */
10176     focusRow : function(row)
10177     {
10178         //Roo.log('GridView.focusRow');
10179         var x = this.bodyEl.dom.scrollLeft;
10180         this.focusCell(row, 0, false);
10181         this.bodyEl.dom.scrollLeft = x;
10182
10183     },
10184      /**
10185      * Focuses the specified cell.
10186      * @param {Number} row The row index
10187      * @param {Number} col The column index
10188      * @param {Boolean} hscroll false to disable horizontal scrolling
10189      */
10190     focusCell : function(row, col, hscroll)
10191     {
10192         //Roo.log('GridView.focusCell');
10193         var el = this.ensureVisible(row, col, hscroll);
10194         // not sure what focusEL achives = it's a <a> pos relative 
10195         //this.focusEl.alignTo(el, "tl-tl");
10196         //if(Roo.isGecko){
10197         //    this.focusEl.focus();
10198         //}else{
10199         //    this.focusEl.focus.defer(1, this.focusEl);
10200         //}
10201     },
10202     
10203      /**
10204      * Scrolls the specified cell into view
10205      * @param {Number} row The row index
10206      * @param {Number} col The column index
10207      * @param {Boolean} hscroll false to disable horizontal scrolling
10208      */
10209     ensureVisible : function(row, col, hscroll)
10210     {
10211         //Roo.log('GridView.ensureVisible,' + row + ',' + col);
10212         //return null; //disable for testing.
10213         if(typeof row != "number"){
10214             row = row.rowIndex;
10215         }
10216         if(row < 0 && row >= this.ds.getCount()){
10217             return  null;
10218         }
10219         col = (col !== undefined ? col : 0);
10220         var cm = this.cm;
10221         while(cm.isHidden(col)){
10222             col++;
10223         }
10224
10225         var el = this.getCellDom(row, col);
10226         if(!el){
10227             return null;
10228         }
10229         var c = this.bodyEl.dom;
10230
10231         var ctop = parseInt(el.offsetTop, 10);
10232         var cleft = parseInt(el.offsetLeft, 10);
10233         var cbot = ctop + el.offsetHeight;
10234         var cright = cleft + el.offsetWidth;
10235
10236         //var ch = c.clientHeight - this.mainHd.dom.offsetHeight;
10237         var ch = 0; //?? header is not withing the area?
10238         var stop = parseInt(c.scrollTop, 10);
10239         var sleft = parseInt(c.scrollLeft, 10);
10240         var sbot = stop + ch;
10241         var sright = sleft + c.clientWidth;
10242         /*
10243         Roo.log('GridView.ensureVisible:' +
10244                 ' ctop:' + ctop +
10245                 ' c.clientHeight:' + c.clientHeight +
10246                 ' this.mainHd.dom.offsetHeight:' + this.mainHd.dom.offsetHeight +
10247                 ' stop:' + stop +
10248                 ' cbot:' + cbot +
10249                 ' sbot:' + sbot +
10250                 ' ch:' + ch  
10251                 );
10252         */
10253         if(ctop < stop){
10254             c.scrollTop = ctop;
10255             //Roo.log("set scrolltop to ctop DISABLE?");
10256         }else if(cbot > sbot){
10257             //Roo.log("set scrolltop to cbot-ch");
10258             c.scrollTop = cbot-ch;
10259         }
10260
10261         if(hscroll !== false){
10262             if(cleft < sleft){
10263                 c.scrollLeft = cleft;
10264             }else if(cright > sright){
10265                 c.scrollLeft = cright-c.clientWidth;
10266             }
10267         }
10268
10269         return el;
10270     },
10271     
10272     
10273     insertRow : function(dm, rowIndex, isUpdate){
10274         
10275         if(!isUpdate){
10276             this.fireEvent("beforerowsinserted", this, rowIndex);
10277         }
10278             //var s = this.getScrollState();
10279         var row = this.renderRow(this.cm, this.store, rowIndex);
10280         // insert before rowIndex..
10281         var e = this.bodyEl.createChild(row,this.getRowDom(rowIndex));
10282         
10283         var _this = this;
10284                 
10285         if(row.cellObjects.length){
10286             Roo.each(row.cellObjects, function(r){
10287                 _this.renderCellObject(r);
10288             })
10289         }
10290             
10291         if(!isUpdate){
10292             this.fireEvent("rowsinserted", this, rowIndex);
10293             //this.syncRowHeights(firstRow, lastRow);
10294             //this.stripeRows(firstRow);
10295             //this.layout();
10296         }
10297         
10298     },
10299     
10300     
10301     getRowDom : function(rowIndex)
10302     {
10303         var rows = this.el.select('tbody > tr', true).elements;
10304         
10305         return (typeof(rows[rowIndex]) == 'undefined') ? false : rows[rowIndex];
10306         
10307     },
10308     getCellDom : function(rowIndex, colIndex)
10309     {
10310         var row = this.getRowDom(rowIndex);
10311         if (row === false) {
10312             return false;
10313         }
10314         var cols = row.select('td', true).elements;
10315         return (typeof(cols[colIndex]) == 'undefined') ? false : cols[colIndex];
10316         
10317     },
10318     
10319     // returns the object tree for a tr..
10320   
10321     
10322     renderRow : function(cm, ds, rowIndex) 
10323     {
10324         var d = ds.getAt(rowIndex);
10325         
10326         var row = {
10327             tag : 'tr',
10328             cls : 'x-row-' + rowIndex,
10329             cn : []
10330         };
10331             
10332         var cellObjects = [];
10333         
10334         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
10335             var config = cm.config[i];
10336             
10337             var renderer = cm.getRenderer(i);
10338             var value = '';
10339             var id = false;
10340             
10341             if(typeof(renderer) !== 'undefined'){
10342                 value = renderer.call(config, d.data[cm.getDataIndex(i)], false, d);
10343             }
10344             // if object are returned, then they are expected to be Roo.bootstrap.Component instances
10345             // and are rendered into the cells after the row is rendered - using the id for the element.
10346             
10347             if(typeof(value) === 'object'){
10348                 id = Roo.id();
10349                 cellObjects.push({
10350                     container : id,
10351                     cfg : value 
10352                 })
10353             }
10354             
10355             var rowcfg = {
10356                 record: d,
10357                 rowIndex : rowIndex,
10358                 colIndex : i,
10359                 rowClass : ''
10360             };
10361
10362             this.fireEvent('rowclass', this, rowcfg);
10363             
10364             var td = {
10365                 tag: 'td',
10366                 // this might end up displaying HTML?
10367                 // this is too messy... - better to only do it on columsn you know are going to be too long
10368                 //tooltip : (typeof(value) === 'object') ? '' : value,
10369                 cls : rowcfg.rowClass + ' x-col-' + i,
10370                 style: '',
10371                 html: (typeof(value) === 'object') ? '' : value
10372             };
10373             
10374             if (id) {
10375                 td.id = id;
10376             }
10377             
10378             if(typeof(config.colspan) != 'undefined'){
10379                 td.colspan = config.colspan;
10380             }
10381             
10382             
10383             
10384             if(typeof(config.align) != 'undefined' && config.align.length){
10385                 td.style += ' text-align:' + config.align + ';';
10386             }
10387             if(typeof(config.valign) != 'undefined' && config.valign.length){
10388                 td.style += ' vertical-align:' + config.valign + ';';
10389             }
10390             /*
10391             if(typeof(config.width) != 'undefined'){
10392                 td.style += ' width:' +  config.width + 'px;';
10393             }
10394             */
10395             
10396             if(typeof(config.cursor) != 'undefined'){
10397                 td.style += ' cursor:' +  config.cursor + ';';
10398             }
10399             
10400             if(typeof(config.cls) != 'undefined'){
10401                 td.cls = (typeof(td.cls) == 'undefined') ? config.cls : (td.cls + ' ' + config.cls);
10402             }
10403             if (this.responsive) {
10404                 ['xs','sm','md','lg'].map(function(size){
10405                     
10406                     if(typeof(config[size]) == 'undefined'){
10407                         return;
10408                     }
10409                     
10410                     
10411                       
10412                     if (!config[size]) { // 0 = hidden
10413                         // BS 4 '0' is treated as hide that column and below.
10414                         td.cls += ' hidden-' + size + ' hidden' + size + '-down';
10415                         return;
10416                     }
10417                     
10418                     td.cls += ' col-' + size + '-' + config[size] + (
10419                         size == 'xs' ? (' col-' +   config[size] ) : '' // bs4 col-{num} replaces col-xs
10420                     );
10421                      
10422     
10423                 });
10424             }
10425             row.cn.push(td);
10426            
10427         }
10428         
10429         row.cellObjects = cellObjects;
10430         
10431         return row;
10432           
10433     },
10434     
10435     
10436     
10437     onBeforeLoad : function()
10438     {
10439         this.el.unmask(); // if needed.
10440     },
10441      /**
10442      * Remove all rows
10443      */
10444     clear : function()
10445     {
10446         this.el.select('tbody', true).first().dom.innerHTML = '';
10447     },
10448     /**
10449      * Show or hide a row.
10450      * @param {Number} rowIndex to show or hide
10451      * @param {Boolean} state hide
10452      */
10453     setRowVisibility : function(rowIndex, state)
10454     {
10455         var bt = this.bodyEl.dom;
10456         
10457         var rows = this.el.select('tbody > tr', true).elements;
10458         
10459         if(typeof(rows[rowIndex]) == 'undefined'){
10460             return;
10461         }
10462         rows[rowIndex][ state ? 'removeClass' : 'addClass']('d-none');
10463         
10464     },
10465     
10466     
10467     getSelectionModel : function(){
10468         if(!this.selModel){
10469             this.selModel = new Roo.bootstrap.Table.RowSelectionModel({grid: this});
10470         }
10471         return this.selModel;
10472     },
10473     /*
10474      * Render the Roo.bootstrap object from renderder
10475      */
10476     renderCellObject : function(r)
10477     {
10478         var _this = this;
10479         
10480         r.cfg.parentId = (typeof(r.container) == 'string') ? r.container : r.container.id;
10481         
10482         var t = r.cfg.render(r.container);
10483         
10484         if(r.cfg.cn){
10485             Roo.each(r.cfg.cn, function(c){
10486                 var child = {
10487                     container: t.getChildContainer(),
10488                     cfg: c
10489                 };
10490                 _this.renderCellObject(child);
10491             })
10492         }
10493     },
10494     /**
10495      * get the Row Index from a dom element.
10496      * @param {Roo.Element} row The row to look for
10497      * @returns {Number} the row
10498      */
10499     getRowIndex : function(row)
10500     {
10501         var rowIndex = -1;
10502         
10503         Roo.each(this.el.select('tbody > tr', true).elements, function(el, index){
10504             if(el != row){
10505                 return;
10506             }
10507             
10508             rowIndex = index;
10509         });
10510         
10511         return rowIndex;
10512     },
10513     /**
10514      * get the header TH element for columnIndex
10515      * @param {Number} columnIndex
10516      * @returns {Roo.Element}
10517      */
10518     getHeaderIndex: function(colIndex)
10519     {
10520         var cols = this.headEl.select('th', true).elements;
10521         return cols[colIndex]; 
10522     },
10523     /**
10524      * get the Column Index from a dom element. (using regex on x-hcol-{colid})
10525      * @param {domElement} cell to look for
10526      * @returns {Number} the column
10527      */
10528     getCellIndex : function(cell)
10529     {
10530         var id = String(cell.className).match(Roo.bootstrap.Table.cellRE);
10531         if(id){
10532             return parseInt(id[1], 10);
10533         }
10534         return 0;
10535     },
10536      /**
10537      * Returns the grid's underlying element = used by panel.Grid
10538      * @return {Element} The element
10539      */
10540     getGridEl : function(){
10541         return this.el;
10542     },
10543      /**
10544      * Forces a resize - used by panel.Grid
10545      * @return {Element} The element
10546      */
10547     autoSize : function()
10548     {
10549         if(this.disableAutoSize) {
10550             return;
10551         }
10552         //var ctr = Roo.get(this.container.dom.parentElement);
10553         var ctr = Roo.get(this.el.dom);
10554         
10555         var thd = this.getGridEl().select('thead',true).first();
10556         var tbd = this.getGridEl().select('tbody', true).first();
10557         var tfd = this.getGridEl().select('tfoot', true).first();
10558         
10559         var cw = ctr.getWidth();
10560         this.getGridEl().select('tfoot tr, tfoot  td',true).setWidth(cw);
10561         
10562         if (tbd) {
10563             
10564             tbd.setWidth(ctr.getWidth());
10565             // if the body has a max height - and then scrolls - we should perhaps set up the height here
10566             // this needs fixing for various usage - currently only hydra job advers I think..
10567             //tdb.setHeight(
10568             //        ctr.getHeight() - ((thd ? thd.getHeight() : 0) + (tfd ? tfd.getHeight() : 0))
10569             //); 
10570             var barsize = (tbd.dom.offsetWidth - tbd.dom.clientWidth);
10571             cw -= barsize;
10572         }
10573         cw = Math.max(cw, this.totalWidth);
10574         this.getGridEl().select('tbody tr',true).setWidth(cw);
10575         this.initCSS();
10576         
10577         // resize 'expandable coloumn?
10578         
10579         return; // we doe not have a view in this design..
10580         
10581     },
10582     onBodyScroll: function()
10583     {
10584         //Roo.log("body scrolled');" + this.bodyEl.dom.scrollLeft);
10585         if(this.headEl){
10586             this.headEl.setStyle({
10587                 'position' : 'relative',
10588                 'left': (-1* this.bodyEl.dom.scrollLeft) + 'px'
10589             });
10590         }
10591         
10592         if(this.lazyLoad){
10593             
10594             var scrollHeight = this.bodyEl.dom.scrollHeight;
10595             
10596             var scrollTop = Math.ceil(this.bodyEl.getScroll().top);
10597             
10598             var height = this.bodyEl.getHeight();
10599             
10600             if(scrollHeight - height == scrollTop) {
10601                 
10602                 var total = this.ds.getTotalCount();
10603                 
10604                 if(this.footer.cursor + this.footer.pageSize < total){
10605                     
10606                     this.footer.ds.load({
10607                         params : {
10608                             start : this.footer.cursor + this.footer.pageSize,
10609                             limit : this.footer.pageSize
10610                         },
10611                         add : true
10612                     });
10613                 }
10614             }
10615             
10616         }
10617     },
10618     onColumnSplitterMoved : function(i, diff)
10619     {
10620         this.userResized = true;
10621         
10622         var cm = this.colModel;
10623         
10624         var w = this.getHeaderIndex(i).getWidth() + diff;
10625         
10626         
10627         cm.setColumnWidth(i, w, true);
10628         this.initCSS();
10629         //var cid = cm.getColumnId(i); << not used in this version?
10630        /* Roo.log(['#' + this.id + ' .x-col-' + i, "width", w + "px"]);
10631         
10632         this.CSS.updateRule( '#' + this.id + ' .x-col-' + i, "width", w + "px");
10633         this.CSS.updateRule('#' + this.id + ' .x-hcol-' + i, "width", w + "px");
10634         this.CSS.updateRule('#' + this.id + ' .x-grid-split-' + i, "left", w + "px");
10635 */
10636         //this.updateSplitters();
10637         //this.layout(); << ??
10638         this.fireEvent("columnresize", i, w);
10639     },
10640     onHeaderChange : function()
10641     {
10642         var header = this.renderHeader();
10643         var table = this.el.select('table', true).first();
10644         
10645         this.headEl.remove();
10646         this.headEl = table.createChild(header, this.bodyEl, false);
10647         
10648         Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
10649             e.on('click', this.sort, this);
10650         }, this);
10651         
10652         if(this.enableColumnResize !== false && Roo.grid.SplitDragZone){
10653             new Roo.grid.SplitDragZone(this, this.headEl.dom, false); // not sure what 'lockedHd is for this implementation..)
10654         }
10655         
10656     },
10657     
10658     onHiddenChange : function(colModel, colIndex, hidden)
10659     {
10660         /*
10661         this.cm.setHidden()
10662         var thSelector = '#' + this.id + ' .x-hcol-' + colIndex;
10663         var tdSelector = '#' + this.id + ' .x-col-' + colIndex;
10664         
10665         this.CSS.updateRule(thSelector, "display", "");
10666         this.CSS.updateRule(tdSelector, "display", "");
10667         
10668         if(hidden){
10669             this.CSS.updateRule(thSelector, "display", "none");
10670             this.CSS.updateRule(tdSelector, "display", "none");
10671         }
10672         */
10673         // onload calls initCSS()
10674         this.onHeaderChange();
10675         this.onLoad();
10676     },
10677     
10678     setColumnWidth: function(col_index, width)
10679     {
10680         // width = "md-2 xs-2..."
10681         if(!this.colModel.config[col_index]) {
10682             return;
10683         }
10684         
10685         var w = width.split(" ");
10686         
10687         var rows = this.el.dom.getElementsByClassName("x-col-"+col_index);
10688         
10689         var h_row = this.el.dom.getElementsByClassName("x-hcol-"+col_index);
10690         
10691         
10692         for(var j = 0; j < w.length; j++) {
10693             
10694             if(!w[j]) {
10695                 continue;
10696             }
10697             
10698             var size_cls = w[j].split("-");
10699             
10700             if(!Number.isInteger(size_cls[1] * 1)) {
10701                 continue;
10702             }
10703             
10704             if(!this.colModel.config[col_index][size_cls[0]]) {
10705                 continue;
10706             }
10707             
10708             if(!h_row[0].classList.contains("col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]])) {
10709                 continue;
10710             }
10711             
10712             h_row[0].classList.replace(
10713                 "col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]],
10714                 "col-"+size_cls[0]+"-"+size_cls[1]
10715             );
10716             
10717             for(var i = 0; i < rows.length; i++) {
10718                 
10719                 var size_cls = w[j].split("-");
10720                 
10721                 if(!Number.isInteger(size_cls[1] * 1)) {
10722                     continue;
10723                 }
10724                 
10725                 if(!this.colModel.config[col_index][size_cls[0]]) {
10726                     continue;
10727                 }
10728                 
10729                 if(!rows[i].classList.contains("col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]])) {
10730                     continue;
10731                 }
10732                 
10733                 rows[i].classList.replace(
10734                     "col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]],
10735                     "col-"+size_cls[0]+"-"+size_cls[1]
10736                 );
10737             }
10738             
10739             this.colModel.config[col_index][size_cls[0]] = size_cls[1];
10740         }
10741     }
10742 });
10743
10744 // currently only used to find the split on drag.. 
10745 Roo.bootstrap.Table.cellRE = /(?:.*?)x-grid-(?:hd|cell|split)-([\d]+)(?:.*?)/;
10746
10747 /**
10748  * @depricated
10749 */
10750 Roo.bootstrap.Table.AbstractSelectionModel = Roo.grid.AbstractSelectionModel;
10751 Roo.bootstrap.Table.RowSelectionModel = Roo.grid.RowSelectionModel;
10752 /*
10753  * - LGPL
10754  *
10755  * table cell
10756  * 
10757  */
10758
10759 /**
10760  * @class Roo.bootstrap.TableCell
10761  * @extends Roo.bootstrap.Component
10762  * @children Roo.bootstrap.Component
10763  * @parent Roo.bootstrap.TableRow
10764  * Bootstrap TableCell class
10765  * 
10766  * @cfg {String} html cell contain text
10767  * @cfg {String} cls cell class
10768  * @cfg {String} tag cell tag (td|th) default td
10769  * @cfg {String} abbr Specifies an abbreviated version of the content in a cell
10770  * @cfg {String} align Aligns the content in a cell
10771  * @cfg {String} axis Categorizes cells
10772  * @cfg {String} bgcolor Specifies the background color of a cell
10773  * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
10774  * @cfg {Number} colspan Specifies the number of columns a cell should span
10775  * @cfg {String} headers Specifies one or more header cells a cell is related to
10776  * @cfg {Number} height Sets the height of a cell
10777  * @cfg {String} nowrap Specifies that the content inside a cell should not wrap
10778  * @cfg {Number} rowspan Sets the number of rows a cell should span
10779  * @cfg {String} scope Defines a way to associate header cells and data cells in a table
10780  * @cfg {String} valign Vertical aligns the content in a cell
10781  * @cfg {Number} width Specifies the width of a cell
10782  * 
10783  * @constructor
10784  * Create a new TableCell
10785  * @param {Object} config The config object
10786  */
10787
10788 Roo.bootstrap.TableCell = function(config){
10789     Roo.bootstrap.TableCell.superclass.constructor.call(this, config);
10790 };
10791
10792 Roo.extend(Roo.bootstrap.TableCell, Roo.bootstrap.Component,  {
10793     
10794     html: false,
10795     cls: false,
10796     tag: false,
10797     abbr: false,
10798     align: false,
10799     axis: false,
10800     bgcolor: false,
10801     charoff: false,
10802     colspan: false,
10803     headers: false,
10804     height: false,
10805     nowrap: false,
10806     rowspan: false,
10807     scope: false,
10808     valign: false,
10809     width: false,
10810     
10811     
10812     getAutoCreate : function(){
10813         var cfg = Roo.apply({}, Roo.bootstrap.TableCell.superclass.getAutoCreate.call(this));
10814         
10815         cfg = {
10816             tag: 'td'
10817         };
10818         
10819         if(this.tag){
10820             cfg.tag = this.tag;
10821         }
10822         
10823         if (this.html) {
10824             cfg.html=this.html
10825         }
10826         if (this.cls) {
10827             cfg.cls=this.cls
10828         }
10829         if (this.abbr) {
10830             cfg.abbr=this.abbr
10831         }
10832         if (this.align) {
10833             cfg.align=this.align
10834         }
10835         if (this.axis) {
10836             cfg.axis=this.axis
10837         }
10838         if (this.bgcolor) {
10839             cfg.bgcolor=this.bgcolor
10840         }
10841         if (this.charoff) {
10842             cfg.charoff=this.charoff
10843         }
10844         if (this.colspan) {
10845             cfg.colspan=this.colspan
10846         }
10847         if (this.headers) {
10848             cfg.headers=this.headers
10849         }
10850         if (this.height) {
10851             cfg.height=this.height
10852         }
10853         if (this.nowrap) {
10854             cfg.nowrap=this.nowrap
10855         }
10856         if (this.rowspan) {
10857             cfg.rowspan=this.rowspan
10858         }
10859         if (this.scope) {
10860             cfg.scope=this.scope
10861         }
10862         if (this.valign) {
10863             cfg.valign=this.valign
10864         }
10865         if (this.width) {
10866             cfg.width=this.width
10867         }
10868         
10869         
10870         return cfg;
10871     }
10872    
10873 });
10874
10875  
10876
10877  /*
10878  * - LGPL
10879  *
10880  * table row
10881  * 
10882  */
10883
10884 /**
10885  * @class Roo.bootstrap.TableRow
10886  * @extends Roo.bootstrap.Component
10887  * @children Roo.bootstrap.TableCell
10888  * @parent Roo.bootstrap.TableBody
10889  * Bootstrap TableRow class
10890  * @cfg {String} cls row class
10891  * @cfg {String} align Aligns the content in a table row
10892  * @cfg {String} bgcolor Specifies a background color for a table row
10893  * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
10894  * @cfg {String} valign Vertical aligns the content in a table row
10895  * 
10896  * @constructor
10897  * Create a new TableRow
10898  * @param {Object} config The config object
10899  */
10900
10901 Roo.bootstrap.TableRow = function(config){
10902     Roo.bootstrap.TableRow.superclass.constructor.call(this, config);
10903 };
10904
10905 Roo.extend(Roo.bootstrap.TableRow, Roo.bootstrap.Component,  {
10906     
10907     cls: false,
10908     align: false,
10909     bgcolor: false,
10910     charoff: false,
10911     valign: false,
10912     
10913     getAutoCreate : function(){
10914         var cfg = Roo.apply({}, Roo.bootstrap.TableRow.superclass.getAutoCreate.call(this));
10915         
10916         cfg = {
10917             tag: 'tr'
10918         };
10919             
10920         if(this.cls){
10921             cfg.cls = this.cls;
10922         }
10923         if(this.align){
10924             cfg.align = this.align;
10925         }
10926         if(this.bgcolor){
10927             cfg.bgcolor = this.bgcolor;
10928         }
10929         if(this.charoff){
10930             cfg.charoff = this.charoff;
10931         }
10932         if(this.valign){
10933             cfg.valign = this.valign;
10934         }
10935         
10936         return cfg;
10937     }
10938    
10939 });
10940
10941  
10942
10943  /*
10944  * - LGPL
10945  *
10946  * table body
10947  * 
10948  */
10949
10950 /**
10951  * @class Roo.bootstrap.TableBody
10952  * @extends Roo.bootstrap.Component
10953  * @children Roo.bootstrap.TableRow
10954  * @parent Roo.bootstrap.Table
10955  * Bootstrap TableBody class
10956  * @cfg {String} cls element class
10957  * @cfg {String} tag element tag (thead|tbody|tfoot) default tbody
10958  * @cfg {String} align Aligns the content inside the element
10959  * @cfg {Number} charoff Sets the number of characters the content inside the element will be aligned from the character specified by the char attribute
10960  * @cfg {String} valign Vertical aligns the content inside the <tbody> element
10961  * 
10962  * @constructor
10963  * Create a new TableBody
10964  * @param {Object} config The config object
10965  */
10966
10967 Roo.bootstrap.TableBody = function(config){
10968     Roo.bootstrap.TableBody.superclass.constructor.call(this, config);
10969 };
10970
10971 Roo.extend(Roo.bootstrap.TableBody, Roo.bootstrap.Component,  {
10972     
10973     cls: false,
10974     tag: false,
10975     align: false,
10976     charoff: false,
10977     valign: false,
10978     
10979     getAutoCreate : function(){
10980         var cfg = Roo.apply({}, Roo.bootstrap.TableBody.superclass.getAutoCreate.call(this));
10981         
10982         cfg = {
10983             tag: 'tbody'
10984         };
10985             
10986         if (this.cls) {
10987             cfg.cls=this.cls
10988         }
10989         if(this.tag){
10990             cfg.tag = this.tag;
10991         }
10992         
10993         if(this.align){
10994             cfg.align = this.align;
10995         }
10996         if(this.charoff){
10997             cfg.charoff = this.charoff;
10998         }
10999         if(this.valign){
11000             cfg.valign = this.valign;
11001         }
11002         
11003         return cfg;
11004     }
11005     
11006     
11007 //    initEvents : function()
11008 //    {
11009 //        
11010 //        if(!this.store){
11011 //            return;
11012 //        }
11013 //        
11014 //        this.store = Roo.factory(this.store, Roo.data);
11015 //        this.store.on('load', this.onLoad, this);
11016 //        
11017 //        this.store.load();
11018 //        
11019 //    },
11020 //    
11021 //    onLoad: function () 
11022 //    {   
11023 //        this.fireEvent('load', this);
11024 //    }
11025 //    
11026 //   
11027 });
11028
11029  
11030
11031  /*
11032  * Based on:
11033  * Ext JS Library 1.1.1
11034  * Copyright(c) 2006-2007, Ext JS, LLC.
11035  *
11036  * Originally Released Under LGPL - original licence link has changed is not relivant.
11037  *
11038  * Fork - LGPL
11039  * <script type="text/javascript">
11040  */
11041
11042 // as we use this in bootstrap.
11043 Roo.namespace('Roo.form');
11044  /**
11045  * @class Roo.form.Action
11046  * Internal Class used to handle form actions
11047  * @constructor
11048  * @param {Roo.form.BasicForm} el The form element or its id
11049  * @param {Object} config Configuration options
11050  */
11051
11052  
11053  
11054 // define the action interface
11055 Roo.form.Action = function(form, options){
11056     this.form = form;
11057     this.options = options || {};
11058 };
11059 /**
11060  * Client Validation Failed
11061  * @const 
11062  */
11063 Roo.form.Action.CLIENT_INVALID = 'client';
11064 /**
11065  * Server Validation Failed
11066  * @const 
11067  */
11068 Roo.form.Action.SERVER_INVALID = 'server';
11069  /**
11070  * Connect to Server Failed
11071  * @const 
11072  */
11073 Roo.form.Action.CONNECT_FAILURE = 'connect';
11074 /**
11075  * Reading Data from Server Failed
11076  * @const 
11077  */
11078 Roo.form.Action.LOAD_FAILURE = 'load';
11079
11080 Roo.form.Action.prototype = {
11081     type : 'default',
11082     failureType : undefined,
11083     response : undefined,
11084     result : undefined,
11085
11086     // interface method
11087     run : function(options){
11088
11089     },
11090
11091     // interface method
11092     success : function(response){
11093
11094     },
11095
11096     // interface method
11097     handleResponse : function(response){
11098
11099     },
11100
11101     // default connection failure
11102     failure : function(response){
11103         
11104         this.response = response;
11105         this.failureType = Roo.form.Action.CONNECT_FAILURE;
11106         this.form.afterAction(this, false);
11107     },
11108
11109     processResponse : function(response){
11110         this.response = response;
11111         if(!response.responseText){
11112             return true;
11113         }
11114         this.result = this.handleResponse(response);
11115         return this.result;
11116     },
11117
11118     // utility functions used internally
11119     getUrl : function(appendParams){
11120         var url = this.options.url || this.form.url || this.form.el.dom.action;
11121         if(appendParams){
11122             var p = this.getParams();
11123             if(p){
11124                 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
11125             }
11126         }
11127         return url;
11128     },
11129
11130     getMethod : function(){
11131         return (this.options.method || this.form.method || this.form.el.dom.method || 'POST').toUpperCase();
11132     },
11133
11134     getParams : function(){
11135         var bp = this.form.baseParams;
11136         var p = this.options.params;
11137         if(p){
11138             if(typeof p == "object"){
11139                 p = Roo.urlEncode(Roo.applyIf(p, bp));
11140             }else if(typeof p == 'string' && bp){
11141                 p += '&' + Roo.urlEncode(bp);
11142             }
11143         }else if(bp){
11144             p = Roo.urlEncode(bp);
11145         }
11146         return p;
11147     },
11148
11149     createCallback : function(){
11150         return {
11151             success: this.success,
11152             failure: this.failure,
11153             scope: this,
11154             timeout: (this.form.timeout*1000),
11155             upload: this.form.fileUpload ? this.success : undefined
11156         };
11157     }
11158 };
11159
11160 Roo.form.Action.Submit = function(form, options){
11161     Roo.form.Action.Submit.superclass.constructor.call(this, form, options);
11162 };
11163
11164 Roo.extend(Roo.form.Action.Submit, Roo.form.Action, {
11165     type : 'submit',
11166
11167     haveProgress : false,
11168     uploadComplete : false,
11169     
11170     // uploadProgress indicator.
11171     uploadProgress : function()
11172     {
11173         if (!this.form.progressUrl) {
11174             return;
11175         }
11176         
11177         if (!this.haveProgress) {
11178             Roo.MessageBox.progress("Uploading", "Uploading");
11179         }
11180         if (this.uploadComplete) {
11181            Roo.MessageBox.hide();
11182            return;
11183         }
11184         
11185         this.haveProgress = true;
11186    
11187         var uid = this.form.findField('UPLOAD_IDENTIFIER').getValue();
11188         
11189         var c = new Roo.data.Connection();
11190         c.request({
11191             url : this.form.progressUrl,
11192             params: {
11193                 id : uid
11194             },
11195             method: 'GET',
11196             success : function(req){
11197                //console.log(data);
11198                 var rdata = false;
11199                 var edata;
11200                 try  {
11201                    rdata = Roo.decode(req.responseText)
11202                 } catch (e) {
11203                     Roo.log("Invalid data from server..");
11204                     Roo.log(edata);
11205                     return;
11206                 }
11207                 if (!rdata || !rdata.success) {
11208                     Roo.log(rdata);
11209                     Roo.MessageBox.alert(Roo.encode(rdata));
11210                     return;
11211                 }
11212                 var data = rdata.data;
11213                 
11214                 if (this.uploadComplete) {
11215                    Roo.MessageBox.hide();
11216                    return;
11217                 }
11218                    
11219                 if (data){
11220                     Roo.MessageBox.updateProgress(data.bytes_uploaded/data.bytes_total,
11221                        Math.floor((data.bytes_total - data.bytes_uploaded)/1000) + 'k remaining'
11222                     );
11223                 }
11224                 this.uploadProgress.defer(2000,this);
11225             },
11226        
11227             failure: function(data) {
11228                 Roo.log('progress url failed ');
11229                 Roo.log(data);
11230             },
11231             scope : this
11232         });
11233            
11234     },
11235     
11236     
11237     run : function()
11238     {
11239         // run get Values on the form, so it syncs any secondary forms.
11240         this.form.getValues();
11241         
11242         var o = this.options;
11243         var method = this.getMethod();
11244         var isPost = method == 'POST';
11245         if(o.clientValidation === false || this.form.isValid()){
11246             
11247             if (this.form.progressUrl) {
11248                 this.form.findField('UPLOAD_IDENTIFIER').setValue(
11249                     (new Date() * 1) + '' + Math.random());
11250                     
11251             } 
11252             
11253             
11254             Roo.Ajax.request(Roo.apply(this.createCallback(), {
11255                 form:this.form.el.dom,
11256                 url:this.getUrl(!isPost),
11257                 method: method,
11258                 params:isPost ? this.getParams() : null,
11259                 isUpload: this.form.fileUpload,
11260                 formData : this.form.formData
11261             }));
11262             
11263             this.uploadProgress();
11264
11265         }else if (o.clientValidation !== false){ // client validation failed
11266             this.failureType = Roo.form.Action.CLIENT_INVALID;
11267             this.form.afterAction(this, false);
11268         }
11269     },
11270
11271     success : function(response)
11272     {
11273         this.uploadComplete= true;
11274         if (this.haveProgress) {
11275             Roo.MessageBox.hide();
11276         }
11277         
11278         
11279         var result = this.processResponse(response);
11280         if(result === true || result.success){
11281             this.form.afterAction(this, true);
11282             return;
11283         }
11284         if(result.errors){
11285             this.form.markInvalid(result.errors);
11286             this.failureType = Roo.form.Action.SERVER_INVALID;
11287         }
11288         this.form.afterAction(this, false);
11289     },
11290     failure : function(response)
11291     {
11292         this.uploadComplete= true;
11293         if (this.haveProgress) {
11294             Roo.MessageBox.hide();
11295         }
11296         
11297         this.response = response;
11298         this.failureType = Roo.form.Action.CONNECT_FAILURE;
11299         this.form.afterAction(this, false);
11300     },
11301     
11302     handleResponse : function(response){
11303         if(this.form.errorReader){
11304             var rs = this.form.errorReader.read(response);
11305             var errors = [];
11306             if(rs.records){
11307                 for(var i = 0, len = rs.records.length; i < len; i++) {
11308                     var r = rs.records[i];
11309                     errors[i] = r.data;
11310                 }
11311             }
11312             if(errors.length < 1){
11313                 errors = null;
11314             }
11315             return {
11316                 success : rs.success,
11317                 errors : errors
11318             };
11319         }
11320         var ret = false;
11321         try {
11322             var rt = response.responseText;
11323             if (rt.match(/^\<!--\[CDATA\[/)) {
11324                 rt = rt.replace(/^\<!--\[CDATA\[/,'');
11325                 rt = rt.replace(/\]\]--\>$/,'');
11326             }
11327             
11328             ret = Roo.decode(rt);
11329         } catch (e) {
11330             ret = {
11331                 success: false,
11332                 errorMsg: "Failed to read server message: " + (response ? response.responseText : ' - no message'),
11333                 errors : []
11334             };
11335         }
11336         return ret;
11337         
11338     }
11339 });
11340
11341
11342 Roo.form.Action.Load = function(form, options){
11343     Roo.form.Action.Load.superclass.constructor.call(this, form, options);
11344     this.reader = this.form.reader;
11345 };
11346
11347 Roo.extend(Roo.form.Action.Load, Roo.form.Action, {
11348     type : 'load',
11349
11350     run : function(){
11351         
11352         Roo.Ajax.request(Roo.apply(
11353                 this.createCallback(), {
11354                     method:this.getMethod(),
11355                     url:this.getUrl(false),
11356                     params:this.getParams()
11357         }));
11358     },
11359
11360     success : function(response){
11361         
11362         var result = this.processResponse(response);
11363         if(result === true || !result.success || !result.data){
11364             this.failureType = Roo.form.Action.LOAD_FAILURE;
11365             this.form.afterAction(this, false);
11366             return;
11367         }
11368         this.form.clearInvalid();
11369         this.form.setValues(result.data);
11370         this.form.afterAction(this, true);
11371     },
11372
11373     handleResponse : function(response){
11374         if(this.form.reader){
11375             var rs = this.form.reader.read(response);
11376             var data = rs.records && rs.records[0] ? rs.records[0].data : null;
11377             return {
11378                 success : rs.success,
11379                 data : data
11380             };
11381         }
11382         return Roo.decode(response.responseText);
11383     }
11384 });
11385
11386 Roo.form.Action.ACTION_TYPES = {
11387     'load' : Roo.form.Action.Load,
11388     'submit' : Roo.form.Action.Submit
11389 };/*
11390  * - LGPL
11391  *
11392  * form
11393  *
11394  */
11395
11396 /**
11397  * @class Roo.bootstrap.form.Form
11398  * @extends Roo.bootstrap.Component
11399  * @children Roo.bootstrap.Component
11400  * Bootstrap Form class
11401  * @cfg {String} method  GET | POST (default POST)
11402  * @cfg {String} labelAlign top | left (default top)
11403  * @cfg {String} align left  | right - for navbars
11404  * @cfg {Boolean} loadMask load mask when submit (default true)
11405
11406  *
11407  * @constructor
11408  * Create a new Form
11409  * @param {Object} config The config object
11410  */
11411
11412
11413 Roo.bootstrap.form.Form = function(config){
11414     
11415     Roo.bootstrap.form.Form.superclass.constructor.call(this, config);
11416     
11417     Roo.bootstrap.form.Form.popover.apply();
11418     
11419     this.addEvents({
11420         /**
11421          * @event clientvalidation
11422          * If the monitorValid config option is true, this event fires repetitively to notify of valid state
11423          * @param {Form} this
11424          * @param {Boolean} valid true if the form has passed client-side validation
11425          */
11426         clientvalidation: true,
11427         /**
11428          * @event beforeaction
11429          * Fires before any action is performed. Return false to cancel the action.
11430          * @param {Form} this
11431          * @param {Action} action The action to be performed
11432          */
11433         beforeaction: true,
11434         /**
11435          * @event actionfailed
11436          * Fires when an action fails.
11437          * @param {Form} this
11438          * @param {Action} action The action that failed
11439          */
11440         actionfailed : true,
11441         /**
11442          * @event actioncomplete
11443          * Fires when an action is completed.
11444          * @param {Form} this
11445          * @param {Action} action The action that completed
11446          */
11447         actioncomplete : true
11448     });
11449 };
11450
11451 Roo.extend(Roo.bootstrap.form.Form, Roo.bootstrap.Component,  {
11452
11453      /**
11454      * @cfg {String} method
11455      * The request method to use (GET or POST) for form actions if one isn't supplied in the action options.
11456      */
11457     method : 'POST',
11458     /**
11459      * @cfg {String} url
11460      * The URL to use for form actions if one isn't supplied in the action options.
11461      */
11462     /**
11463      * @cfg {Boolean} fileUpload
11464      * Set to true if this form is a file upload.
11465      */
11466
11467     /**
11468      * @cfg {Object} baseParams
11469      * Parameters to pass with all requests. e.g. baseParams: {id: '123', foo: 'bar'}.
11470      */
11471
11472     /**
11473      * @cfg {Number} timeout Timeout for form actions in seconds (default is 30 seconds).
11474      */
11475     timeout: 30,
11476     /**
11477      * @cfg {Sting} align (left|right) for navbar forms
11478      */
11479     align : 'left',
11480
11481     // private
11482     activeAction : null,
11483
11484     /**
11485      * By default wait messages are displayed with Roo.MessageBox.wait. You can target a specific
11486      * element by passing it or its id or mask the form itself by passing in true.
11487      * @type Mixed
11488      */
11489     waitMsgTarget : false,
11490
11491     loadMask : true,
11492     
11493     /**
11494      * @cfg {Boolean} errorMask (true|false) default false
11495      */
11496     errorMask : false,
11497     
11498     /**
11499      * @cfg {Number} maskOffset Default 100
11500      */
11501     maskOffset : 100,
11502     
11503     /**
11504      * @cfg {Boolean} maskBody
11505      */
11506     maskBody : false,
11507
11508     getAutoCreate : function(){
11509
11510         var cfg = {
11511             tag: 'form',
11512             method : this.method || 'POST',
11513             id : this.id || Roo.id(),
11514             cls : ''
11515         };
11516         if (this.parent().xtype.match(/^Nav/)) {
11517             cfg.cls = 'navbar-form form-inline navbar-' + this.align;
11518
11519         }
11520
11521         if (this.labelAlign == 'left' ) {
11522             cfg.cls += ' form-horizontal';
11523         }
11524
11525
11526         return cfg;
11527     },
11528     initEvents : function()
11529     {
11530         this.el.on('submit', this.onSubmit, this);
11531         // this was added as random key presses on the form where triggering form submit.
11532         this.el.on('keypress', function(e) {
11533             if (e.getCharCode() != 13) {
11534                 return true;
11535             }
11536             // we might need to allow it for textareas.. and some other items.
11537             // check e.getTarget().
11538
11539             if(e.getTarget().nodeName.toLowerCase() === 'textarea'){
11540                 return true;
11541             }
11542
11543             Roo.log("keypress blocked");
11544
11545             e.preventDefault();
11546             return false;
11547         });
11548         
11549     },
11550     // private
11551     onSubmit : function(e){
11552         e.stopEvent();
11553     },
11554
11555      /**
11556      * Returns true if client-side validation on the form is successful.
11557      * @return Boolean
11558      */
11559     isValid : function(){
11560         var items = this.getItems();
11561         var valid = true;
11562         var target = false;
11563         
11564         items.each(function(f){
11565             
11566             if(f.validate()){
11567                 return;
11568             }
11569             
11570             Roo.log('invalid field: ' + f.name);
11571             
11572             valid = false;
11573
11574             if(!target && f.el.isVisible(true)){
11575                 target = f;
11576             }
11577            
11578         });
11579         
11580         if(this.errorMask && !valid){
11581             Roo.bootstrap.form.Form.popover.mask(this, target);
11582         }
11583         
11584         return valid;
11585     },
11586     
11587     /**
11588      * Returns true if any fields in this form have changed since their original load.
11589      * @return Boolean
11590      */
11591     isDirty : function(){
11592         var dirty = false;
11593         var items = this.getItems();
11594         items.each(function(f){
11595            if(f.isDirty()){
11596                dirty = true;
11597                return false;
11598            }
11599            return true;
11600         });
11601         return dirty;
11602     },
11603      /**
11604      * Performs a predefined action (submit or load) or custom actions you define on this form.
11605      * @param {String} actionName The name of the action type
11606      * @param {Object} options (optional) The options to pass to the action.  All of the config options listed
11607      * below are supported by both the submit and load actions unless otherwise noted (custom actions could also
11608      * accept other config options):
11609      * <pre>
11610 Property          Type             Description
11611 ----------------  ---------------  ----------------------------------------------------------------------------------
11612 url               String           The url for the action (defaults to the form's url)
11613 method            String           The form method to use (defaults to the form's method, or POST if not defined)
11614 params            String/Object    The params to pass (defaults to the form's baseParams, or none if not defined)
11615 clientValidation  Boolean          Applies to submit only.  Pass true to call form.isValid() prior to posting to
11616                                    validate the form on the client (defaults to false)
11617      * </pre>
11618      * @return {BasicForm} this
11619      */
11620     doAction : function(action, options){
11621         if(typeof action == 'string'){
11622             action = new Roo.form.Action.ACTION_TYPES[action](this, options);
11623         }
11624         if(this.fireEvent('beforeaction', this, action) !== false){
11625             this.beforeAction(action);
11626             action.run.defer(100, action);
11627         }
11628         return this;
11629     },
11630
11631     // private
11632     beforeAction : function(action){
11633         var o = action.options;
11634         
11635         if(this.loadMask){
11636             
11637             if(this.maskBody){
11638                 Roo.get(document.body).mask(o.waitMsg || "Sending", 'x-mask-loading')
11639             } else {
11640                 this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
11641             }
11642         }
11643         // not really supported yet.. ??
11644
11645         //if(this.waitMsgTarget === true){
11646         //  this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
11647         //}else if(this.waitMsgTarget){
11648         //    this.waitMsgTarget = Roo.get(this.waitMsgTarget);
11649         //    this.waitMsgTarget.mask(o.waitMsg || "Sending", 'x-mask-loading');
11650         //}else {
11651         //    Roo.MessageBox.wait(o.waitMsg || "Sending", o.waitTitle || this.waitTitle || 'Please Wait...');
11652        // }
11653
11654     },
11655
11656     // private
11657     afterAction : function(action, success){
11658         this.activeAction = null;
11659         var o = action.options;
11660
11661         if(this.loadMask){
11662             
11663             if(this.maskBody){
11664                 Roo.get(document.body).unmask();
11665             } else {
11666                 this.el.unmask();
11667             }
11668         }
11669         
11670         //if(this.waitMsgTarget === true){
11671 //            this.el.unmask();
11672         //}else if(this.waitMsgTarget){
11673         //    this.waitMsgTarget.unmask();
11674         //}else{
11675         //    Roo.MessageBox.updateProgress(1);
11676         //    Roo.MessageBox.hide();
11677        // }
11678         //
11679         if(success){
11680             if(o.reset){
11681                 this.reset();
11682             }
11683             Roo.callback(o.success, o.scope, [this, action]);
11684             this.fireEvent('actioncomplete', this, action);
11685
11686         }else{
11687
11688             // failure condition..
11689             // we have a scenario where updates need confirming.
11690             // eg. if a locking scenario exists..
11691             // we look for { errors : { needs_confirm : true }} in the response.
11692             if (
11693                 (typeof(action.result) != 'undefined')  &&
11694                 (typeof(action.result.errors) != 'undefined')  &&
11695                 (typeof(action.result.errors.needs_confirm) != 'undefined')
11696            ){
11697                 var _t = this;
11698                 Roo.log("not supported yet");
11699                  /*
11700
11701                 Roo.MessageBox.confirm(
11702                     "Change requires confirmation",
11703                     action.result.errorMsg,
11704                     function(r) {
11705                         if (r != 'yes') {
11706                             return;
11707                         }
11708                         _t.doAction('submit', { params :  { _submit_confirmed : 1 } }  );
11709                     }
11710
11711                 );
11712                 */
11713
11714
11715                 return;
11716             }
11717
11718             Roo.callback(o.failure, o.scope, [this, action]);
11719             // show an error message if no failed handler is set..
11720             if (!this.hasListener('actionfailed')) {
11721                 Roo.log("need to add dialog support");
11722                 /*
11723                 Roo.MessageBox.alert("Error",
11724                     (typeof(action.result) != 'undefined' && typeof(action.result.errorMsg) != 'undefined') ?
11725                         action.result.errorMsg :
11726                         "Saving Failed, please check your entries or try again"
11727                 );
11728                 */
11729             }
11730
11731             this.fireEvent('actionfailed', this, action);
11732         }
11733
11734     },
11735     /**
11736      * Find a Roo.form.Field in this form by id, dataIndex, name or hiddenName
11737      * @param {String} id The value to search for
11738      * @return Field
11739      */
11740     findField : function(id){
11741         var items = this.getItems();
11742         var field = items.get(id);
11743         if(!field){
11744              items.each(function(f){
11745                 if(f.isFormField && (f.dataIndex == id || f.id == id || f.getName() == id)){
11746                     field = f;
11747                     return false;
11748                 }
11749                 return true;
11750             });
11751         }
11752         return field || null;
11753     },
11754      /**
11755      * Mark fields in this form invalid in bulk.
11756      * @param {Array/Object} errors Either an array in the form [{id:'fieldId', msg:'The message'},...] or an object hash of {id: msg, id2: msg2}
11757      * @return {BasicForm} this
11758      */
11759     markInvalid : function(errors){
11760         if(errors instanceof Array){
11761             for(var i = 0, len = errors.length; i < len; i++){
11762                 var fieldError = errors[i];
11763                 var f = this.findField(fieldError.id);
11764                 if(f){
11765                     f.markInvalid(fieldError.msg);
11766                 }
11767             }
11768         }else{
11769             var field, id;
11770             for(id in errors){
11771                 if(typeof errors[id] != 'function' && (field = this.findField(id))){
11772                     field.markInvalid(errors[id]);
11773                 }
11774             }
11775         }
11776         //Roo.each(this.childForms || [], function (f) {
11777         //    f.markInvalid(errors);
11778         //});
11779
11780         return this;
11781     },
11782
11783     /**
11784      * Set values for fields in this form in bulk.
11785      * @param {Array/Object} values Either an array in the form [{id:'fieldId', value:'foo'},...] or an object hash of {id: value, id2: value2}
11786      * @return {BasicForm} this
11787      */
11788     setValues : function(values){
11789         if(values instanceof Array){ // array of objects
11790             for(var i = 0, len = values.length; i < len; i++){
11791                 var v = values[i];
11792                 var f = this.findField(v.id);
11793                 if(f){
11794                     f.setValue(v.value);
11795                     if(this.trackResetOnLoad){
11796                         f.originalValue = f.getValue();
11797                     }
11798                 }
11799             }
11800         }else{ // object hash
11801             var field, id;
11802             for(id in values){
11803                 if(typeof values[id] != 'function' && (field = this.findField(id))){
11804
11805                     if (field.setFromData &&
11806                         field.valueField &&
11807                         field.displayField &&
11808                         // combos' with local stores can
11809                         // be queried via setValue()
11810                         // to set their value..
11811                         (field.store && !field.store.isLocal)
11812                         ) {
11813                         // it's a combo
11814                         var sd = { };
11815                         sd[field.valueField] = typeof(values[field.hiddenName]) == 'undefined' ? '' : values[field.hiddenName];
11816                         sd[field.displayField] = typeof(values[field.name]) == 'undefined' ? '' : values[field.name];
11817                         field.setFromData(sd);
11818
11819                     } else if(field.setFromData && (field.store && !field.store.isLocal)) {
11820                         
11821                         field.setFromData(values);
11822                         
11823                     } else {
11824                         field.setValue(values[id]);
11825                     }
11826
11827
11828                     if(this.trackResetOnLoad){
11829                         field.originalValue = field.getValue();
11830                     }
11831                 }
11832             }
11833         }
11834
11835         //Roo.each(this.childForms || [], function (f) {
11836         //    f.setValues(values);
11837         //});
11838
11839         return this;
11840     },
11841
11842     /**
11843      * Returns the fields in this form as an object with key/value pairs. If multiple fields exist with the same name
11844      * they are returned as an array.
11845      * @param {Boolean} asString
11846      * @return {Object}
11847      */
11848     getValues : function(asString){
11849         //if (this.childForms) {
11850             // copy values from the child forms
11851         //    Roo.each(this.childForms, function (f) {
11852         //        this.setValues(f.getValues());
11853         //    }, this);
11854         //}
11855
11856
11857
11858         var fs = Roo.lib.Ajax.serializeForm(this.el.dom);
11859         if(asString === true){
11860             return fs;
11861         }
11862         return Roo.urlDecode(fs);
11863     },
11864
11865     /**
11866      * Returns the fields in this form as an object with key/value pairs.
11867      * This differs from getValues as it calls getValue on each child item, rather than using dom data.
11868      * @return {Object}
11869      */
11870     getFieldValues : function(with_hidden)
11871     {
11872         var items = this.getItems();
11873         var ret = {};
11874         items.each(function(f){
11875             
11876             if (!f.getName()) {
11877                 return;
11878             }
11879             
11880             var v = f.getValue();
11881             
11882             if (f.inputType =='radio') {
11883                 if (typeof(ret[f.getName()]) == 'undefined') {
11884                     ret[f.getName()] = ''; // empty..
11885                 }
11886
11887                 if (!f.el.dom.checked) {
11888                     return;
11889
11890                 }
11891                 v = f.el.dom.value;
11892
11893             }
11894             
11895             if(f.xtype == 'MoneyField'){
11896                 ret[f.currencyName] = f.getCurrency();
11897             }
11898
11899             // not sure if this supported any more..
11900             if ((typeof(v) == 'object') && f.getRawValue) {
11901                 v = f.getRawValue() ; // dates..
11902             }
11903             // combo boxes where name != hiddenName...
11904             if (f.name !== false && f.name != '' && f.name != f.getName()) {
11905                 ret[f.name] = f.getRawValue();
11906             }
11907             ret[f.getName()] = v;
11908         });
11909
11910         return ret;
11911     },
11912
11913     /**
11914      * Clears all invalid messages in this form.
11915      * @return {BasicForm} this
11916      */
11917     clearInvalid : function(){
11918         var items = this.getItems();
11919
11920         items.each(function(f){
11921            f.clearInvalid();
11922         });
11923
11924         return this;
11925     },
11926
11927     /**
11928      * Resets this form.
11929      * @return {BasicForm} this
11930      */
11931     reset : function(){
11932         var items = this.getItems();
11933         items.each(function(f){
11934             f.reset();
11935         });
11936
11937         Roo.each(this.childForms || [], function (f) {
11938             f.reset();
11939         });
11940
11941
11942         return this;
11943     },
11944     
11945     getItems : function()
11946     {
11947         var r=new Roo.util.MixedCollection(false, function(o){
11948             return o.id || (o.id = Roo.id());
11949         });
11950         var iter = function(el) {
11951             if (el.inputEl) {
11952                 r.add(el);
11953             }
11954             if (!el.items) {
11955                 return;
11956             }
11957             Roo.each(el.items,function(e) {
11958                 iter(e);
11959             });
11960         };
11961
11962         iter(this);
11963         return r;
11964     },
11965     
11966     hideFields : function(items)
11967     {
11968         Roo.each(items, function(i){
11969             
11970             var f = this.findField(i);
11971             
11972             if(!f){
11973                 return;
11974             }
11975             
11976             f.hide();
11977             
11978         }, this);
11979     },
11980     
11981     showFields : function(items)
11982     {
11983         Roo.each(items, function(i){
11984             
11985             var f = this.findField(i);
11986             
11987             if(!f){
11988                 return;
11989             }
11990             
11991             f.show();
11992             
11993         }, this);
11994     }
11995
11996 });
11997
11998 Roo.apply(Roo.bootstrap.form.Form, {
11999     
12000     popover : {
12001         
12002         padding : 5,
12003         
12004         isApplied : false,
12005         
12006         isMasked : false,
12007         
12008         form : false,
12009         
12010         target : false,
12011         
12012         toolTip : false,
12013         
12014         intervalID : false,
12015         
12016         maskEl : false,
12017         
12018         apply : function()
12019         {
12020             if(this.isApplied){
12021                 return;
12022             }
12023             
12024             this.maskEl = {
12025                 top : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-top-mask" }, true),
12026                 left : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-left-mask" }, true),
12027                 bottom : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-bottom-mask" }, true),
12028                 right : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-right-mask" }, true)
12029             };
12030             
12031             this.maskEl.top.enableDisplayMode("block");
12032             this.maskEl.left.enableDisplayMode("block");
12033             this.maskEl.bottom.enableDisplayMode("block");
12034             this.maskEl.right.enableDisplayMode("block");
12035             
12036             this.toolTip = new Roo.bootstrap.Tooltip({
12037                 cls : 'roo-form-error-popover',
12038                 alignment : {
12039                     'left' : ['r-l', [-2,0], 'right'],
12040                     'right' : ['l-r', [2,0], 'left'],
12041                     'bottom' : ['tl-bl', [0,2], 'top'],
12042                     'top' : [ 'bl-tl', [0,-2], 'bottom']
12043                 }
12044             });
12045             
12046             this.toolTip.render(Roo.get(document.body));
12047
12048             this.toolTip.el.enableDisplayMode("block");
12049             
12050             Roo.get(document.body).on('click', function(){
12051                 this.unmask();
12052             }, this);
12053             
12054             Roo.get(document.body).on('touchstart', function(){
12055                 this.unmask();
12056             }, this);
12057             
12058             this.isApplied = true
12059         },
12060         
12061         mask : function(form, target)
12062         {
12063             this.form = form;
12064             
12065             this.target = target;
12066             
12067             if(!this.form.errorMask || !target.el){
12068                 return;
12069             }
12070             
12071             var scrollable = this.target.el.findScrollableParent() || this.target.el.findParent('div.modal', 100, true) || Roo.get(document.body);
12072             
12073             Roo.log(scrollable);
12074             
12075             var ot = this.target.el.calcOffsetsTo(scrollable);
12076             
12077             var scrollTo = ot[1] - this.form.maskOffset;
12078             
12079             scrollTo = Math.min(scrollTo, scrollable.dom.scrollHeight);
12080             
12081             scrollable.scrollTo('top', scrollTo);
12082             
12083             var box = this.target.el.getBox();
12084             Roo.log(box);
12085             var zIndex = Roo.bootstrap.Modal.zIndex++;
12086
12087             
12088             this.maskEl.top.setStyle('position', 'absolute');
12089             this.maskEl.top.setStyle('z-index', zIndex);
12090             this.maskEl.top.setSize(Roo.lib.Dom.getDocumentWidth(), box.y - this.padding);
12091             this.maskEl.top.setLeft(0);
12092             this.maskEl.top.setTop(0);
12093             this.maskEl.top.show();
12094             
12095             this.maskEl.left.setStyle('position', 'absolute');
12096             this.maskEl.left.setStyle('z-index', zIndex);
12097             this.maskEl.left.setSize(box.x - this.padding, box.height + this.padding * 2);
12098             this.maskEl.left.setLeft(0);
12099             this.maskEl.left.setTop(box.y - this.padding);
12100             this.maskEl.left.show();
12101
12102             this.maskEl.bottom.setStyle('position', 'absolute');
12103             this.maskEl.bottom.setStyle('z-index', zIndex);
12104             this.maskEl.bottom.setSize(Roo.lib.Dom.getDocumentWidth(), Roo.lib.Dom.getDocumentHeight() - box.bottom - this.padding);
12105             this.maskEl.bottom.setLeft(0);
12106             this.maskEl.bottom.setTop(box.bottom + this.padding);
12107             this.maskEl.bottom.show();
12108
12109             this.maskEl.right.setStyle('position', 'absolute');
12110             this.maskEl.right.setStyle('z-index', zIndex);
12111             this.maskEl.right.setSize(Roo.lib.Dom.getDocumentWidth() - box.right - this.padding, box.height + this.padding * 2);
12112             this.maskEl.right.setLeft(box.right + this.padding);
12113             this.maskEl.right.setTop(box.y - this.padding);
12114             this.maskEl.right.show();
12115
12116             this.toolTip.bindEl = this.target.el;
12117
12118             this.toolTip.el.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
12119
12120             var tip = this.target.blankText;
12121
12122             if(this.target.getValue() !== '' ) {
12123                 
12124                 if (this.target.invalidText.length) {
12125                     tip = this.target.invalidText;
12126                 } else if (this.target.regexText.length){
12127                     tip = this.target.regexText;
12128                 }
12129             }
12130
12131             this.toolTip.show(tip);
12132
12133             this.intervalID = window.setInterval(function() {
12134                 Roo.bootstrap.form.Form.popover.unmask();
12135             }, 10000);
12136
12137             window.onwheel = function(){ return false;};
12138             
12139             (function(){ this.isMasked = true; }).defer(500, this);
12140             
12141         },
12142         
12143         unmask : function()
12144         {
12145             if(!this.isApplied || !this.isMasked || !this.form || !this.target || !this.form.errorMask){
12146                 return;
12147             }
12148             
12149             this.maskEl.top.setStyle('position', 'absolute');
12150             this.maskEl.top.setSize(0, 0).setXY([0, 0]);
12151             this.maskEl.top.hide();
12152
12153             this.maskEl.left.setStyle('position', 'absolute');
12154             this.maskEl.left.setSize(0, 0).setXY([0, 0]);
12155             this.maskEl.left.hide();
12156
12157             this.maskEl.bottom.setStyle('position', 'absolute');
12158             this.maskEl.bottom.setSize(0, 0).setXY([0, 0]);
12159             this.maskEl.bottom.hide();
12160
12161             this.maskEl.right.setStyle('position', 'absolute');
12162             this.maskEl.right.setSize(0, 0).setXY([0, 0]);
12163             this.maskEl.right.hide();
12164             
12165             this.toolTip.hide();
12166             
12167             this.toolTip.el.hide();
12168             
12169             window.onwheel = function(){ return true;};
12170             
12171             if(this.intervalID){
12172                 window.clearInterval(this.intervalID);
12173                 this.intervalID = false;
12174             }
12175             
12176             this.isMasked = false;
12177             
12178         }
12179         
12180     }
12181     
12182 });
12183
12184 /*
12185  * Based on:
12186  * Ext JS Library 1.1.1
12187  * Copyright(c) 2006-2007, Ext JS, LLC.
12188  *
12189  * Originally Released Under LGPL - original licence link has changed is not relivant.
12190  *
12191  * Fork - LGPL
12192  * <script type="text/javascript">
12193  */
12194 /**
12195  * @class Roo.form.VTypes
12196  * Overridable validation definitions. The validations provided are basic and intended to be easily customizable and extended.
12197  * @static
12198  */
12199 Roo.form.VTypes = function(){
12200     // closure these in so they are only created once.
12201     var alpha = /^[a-zA-Z_]+$/;
12202     var alphanum = /^[a-zA-Z0-9_]+$/;
12203     var email = /^([\w]+)(.[\w]+)*@([\w-]+\.){1,5}([A-Za-z]){2,24}$/;
12204     var url = /(((https?)|(ftp)):\/\/([\-\w]+\.)+\w{2,3}(\/[%\-\w]+(\.\w{2,})?)*(([\w\-\.\?\\\/+@&#;`~=%!]*)(\.\w{2,})?)*\/?)/i;
12205
12206     // All these messages and functions are configurable
12207     return {
12208         /**
12209          * The function used to validate email addresses
12210          * @param {String} value The email address
12211          */
12212         email : function(v){
12213             return email.test(v);
12214         },
12215         /**
12216          * The error text to display when the email validation function returns false
12217          * @type String
12218          */
12219         emailText : 'This field should be an e-mail address in the format "user@domain.com"',
12220         /**
12221          * The keystroke filter mask to be applied on email input
12222          * @type RegExp
12223          */
12224         emailMask : /[a-z0-9_\.\-@]/i,
12225
12226         /**
12227          * The function used to validate URLs
12228          * @param {String} value The URL
12229          */
12230         url : function(v){
12231             return url.test(v);
12232         },
12233         /**
12234          * The error text to display when the url validation function returns false
12235          * @type String
12236          */
12237         urlText : 'This field should be a URL in the format "http:/'+'/www.domain.com"',
12238         
12239         /**
12240          * The function used to validate alpha values
12241          * @param {String} value The value
12242          */
12243         alpha : function(v){
12244             return alpha.test(v);
12245         },
12246         /**
12247          * The error text to display when the alpha validation function returns false
12248          * @type String
12249          */
12250         alphaText : 'This field should only contain letters and _',
12251         /**
12252          * The keystroke filter mask to be applied on alpha input
12253          * @type RegExp
12254          */
12255         alphaMask : /[a-z_]/i,
12256
12257         /**
12258          * The function used to validate alphanumeric values
12259          * @param {String} value The value
12260          */
12261         alphanum : function(v){
12262             return alphanum.test(v);
12263         },
12264         /**
12265          * The error text to display when the alphanumeric validation function returns false
12266          * @type String
12267          */
12268         alphanumText : 'This field should only contain letters, numbers and _',
12269         /**
12270          * The keystroke filter mask to be applied on alphanumeric input
12271          * @type RegExp
12272          */
12273         alphanumMask : /[a-z0-9_]/i
12274     };
12275 }();/*
12276  * - LGPL
12277  *
12278  * Input
12279  * 
12280  */
12281
12282 /**
12283  * @class Roo.bootstrap.form.Input
12284  * @extends Roo.bootstrap.Component
12285  * Bootstrap Input class
12286  * @cfg {Boolean} disabled is it disabled
12287  * @cfg {String} inputType (button|checkbox|email|file|hidden|image|number|password|radio|range|reset|search|submit|text)  
12288  * @cfg {String} name name of the input
12289  * @cfg {string} fieldLabel - the label associated
12290  * @cfg {string} placeholder - placeholder to put in text.
12291  * @cfg {string} before - input group add on before
12292  * @cfg {string} after - input group add on after
12293  * @cfg {string} size - (lg|sm) or leave empty..
12294  * @cfg {Number} xs colspan out of 12 for mobile-sized screens
12295  * @cfg {Number} sm colspan out of 12 for tablet-sized screens
12296  * @cfg {Number} md colspan out of 12 for computer-sized screens
12297  * @cfg {Number} lg colspan out of 12 for large computer-sized screens
12298  * @cfg {string} value default value of the input
12299  * @cfg {Number} labelWidth set the width of label 
12300  * @cfg {Number} labellg set the width of label (1-12)
12301  * @cfg {Number} labelmd set the width of label (1-12)
12302  * @cfg {Number} labelsm set the width of label (1-12)
12303  * @cfg {Number} labelxs set the width of label (1-12)
12304  * @cfg {String} labelAlign (top|left)
12305  * @cfg {Boolean} readOnly Specifies that the field should be read-only
12306  * @cfg {String} autocomplete - default is new-password see: https://developers.google.com/web/fundamentals/input/form/label-and-name-inputs?hl=en
12307  * @cfg {String} indicatorpos (left|right) default left
12308  * @cfg {String} capture (user|camera) use for file input only. (default empty)
12309  * @cfg {String} accept (image|video|audio) use for file input only. (default empty)
12310  * @cfg {Boolean} preventMark Do not show tick or cross if error/success
12311  * @cfg {Roo.bootstrap.Button} before Button to show before
12312  * @cfg {Roo.bootstrap.Button} afterButton to show before
12313  * @cfg {String} align (left|center|right) Default left
12314  * @cfg {Boolean} forceFeedback (true|false) Default false
12315  * 
12316  * @constructor
12317  * Create a new Input
12318  * @param {Object} config The config object
12319  */
12320
12321 Roo.bootstrap.form.Input = function(config){
12322     
12323     Roo.bootstrap.form.Input.superclass.constructor.call(this, config);
12324     
12325     this.addEvents({
12326         /**
12327          * @event focus
12328          * Fires when this field receives input focus.
12329          * @param {Roo.form.Field} this
12330          */
12331         focus : true,
12332         /**
12333          * @event blur
12334          * Fires when this field loses input focus.
12335          * @param {Roo.form.Field} this
12336          */
12337         blur : true,
12338         /**
12339          * @event specialkey
12340          * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed.  You can check
12341          * {@link Roo.EventObject#getKey} to determine which key was pressed.
12342          * @param {Roo.form.Field} this
12343          * @param {Roo.EventObject} e The event object
12344          */
12345         specialkey : true,
12346         /**
12347          * @event change
12348          * Fires just before the field blurs if the field value has changed.
12349          * @param {Roo.form.Field} this
12350          * @param {Mixed} newValue The new value
12351          * @param {Mixed} oldValue The original value
12352          */
12353         change : true,
12354         /**
12355          * @event invalid
12356          * Fires after the field has been marked as invalid.
12357          * @param {Roo.form.Field} this
12358          * @param {String} msg The validation message
12359          */
12360         invalid : true,
12361         /**
12362          * @event valid
12363          * Fires after the field has been validated with no errors.
12364          * @param {Roo.form.Field} this
12365          */
12366         valid : true,
12367          /**
12368          * @event keyup
12369          * Fires after the key up
12370          * @param {Roo.form.Field} this
12371          * @param {Roo.EventObject}  e The event Object
12372          */
12373         keyup : true,
12374         /**
12375          * @event paste
12376          * Fires after the user pastes into input
12377          * @param {Roo.form.Field} this
12378          * @param {Roo.EventObject}  e The event Object
12379          */
12380         paste : true
12381     });
12382 };
12383
12384 Roo.extend(Roo.bootstrap.form.Input, Roo.bootstrap.Component,  {
12385      /**
12386      * @cfg {String/Boolean} validationEvent The event that should initiate field validation. Set to false to disable
12387       automatic validation (defaults to "keyup").
12388      */
12389     validationEvent : "keyup",
12390      /**
12391      * @cfg {Boolean} validateOnBlur Whether the field should validate when it loses focus (defaults to true).
12392      */
12393     validateOnBlur : true,
12394     /**
12395      * @cfg {Number} validationDelay The length of time in milliseconds after user input begins until validation is initiated (defaults to 250)
12396      */
12397     validationDelay : 250,
12398      /**
12399      * @cfg {String} focusClass The CSS class to use when the field receives focus (defaults to "x-form-focus")
12400      */
12401     focusClass : "x-form-focus",  // not needed???
12402     
12403        
12404     /**
12405      * @cfg {String} invalidClass DEPRICATED - code uses BS4 - is-valid / is-invalid
12406      */
12407     invalidClass : "has-warning",
12408     
12409     /**
12410      * @cfg {String} validClass DEPRICATED - code uses BS4 - is-valid / is-invalid
12411      */
12412     validClass : "has-success",
12413     
12414     /**
12415      * @cfg {Boolean} hasFeedback (true|false) default true
12416      */
12417     hasFeedback : true,
12418     
12419     /**
12420      * @cfg {String} invalidFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
12421      */
12422     invalidFeedbackClass : "glyphicon-warning-sign",
12423     
12424     /**
12425      * @cfg {String} validFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
12426      */
12427     validFeedbackClass : "glyphicon-ok",
12428     
12429     /**
12430      * @cfg {Boolean} selectOnFocus True to automatically select any existing field text when the field receives input focus (defaults to false)
12431      */
12432     selectOnFocus : false,
12433     
12434      /**
12435      * @cfg {String} maskRe An input mask regular expression that will be used to filter keystrokes that don't match (defaults to null)
12436      */
12437     maskRe : null,
12438        /**
12439      * @cfg {String} vtype A validation type name as defined in {@link Roo.form.VTypes} (defaults to null)
12440      */
12441     vtype : null,
12442     
12443       /**
12444      * @cfg {Boolean} disableKeyFilter True to disable input keystroke filtering (defaults to false)
12445      */
12446     disableKeyFilter : false,
12447     
12448        /**
12449      * @cfg {Boolean} disabled True to disable the field (defaults to false).
12450      */
12451     disabled : false,
12452      /**
12453      * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to true)
12454      */
12455     allowBlank : true,
12456     /**
12457      * @cfg {String} blankText Error text to display if the allow blank validation fails (defaults to "This field is required")
12458      */
12459     blankText : "Please complete this mandatory field",
12460     
12461      /**
12462      * @cfg {Number} minLength Minimum input field length required (defaults to 0)
12463      */
12464     minLength : 0,
12465     /**
12466      * @cfg {Number} maxLength Maximum input field length allowed (defaults to Number.MAX_VALUE)
12467      */
12468     maxLength : Number.MAX_VALUE,
12469     /**
12470      * @cfg {String} minLengthText Error text to display if the minimum length validation fails (defaults to "The minimum length for this field is {minLength}")
12471      */
12472     minLengthText : "The minimum length for this field is {0}",
12473     /**
12474      * @cfg {String} maxLengthText Error text to display if the maximum length validation fails (defaults to "The maximum length for this field is {maxLength}")
12475      */
12476     maxLengthText : "The maximum length for this field is {0}",
12477   
12478     
12479     /**
12480      * @cfg {Function} validator A custom validation function to be called during field validation (defaults to null).
12481      * If available, this function will be called only after the basic validators all return true, and will be passed the
12482      * current field value and expected to return boolean true if the value is valid or a string error message if invalid.
12483      */
12484     validator : null,
12485     /**
12486      * @cfg {RegExp} regex A JavaScript RegExp object to be tested against the field value during validation (defaults to null).
12487      * If available, this regex will be evaluated only after the basic validators all return true, and will be passed the
12488      * current field value.  If the test fails, the field will be marked invalid using {@link #regexText}.
12489      */
12490     regex : null,
12491     /**
12492      * @cfg {String} regexText -- Depricated - use Invalid Text
12493      */
12494     regexText : "",
12495     
12496     /**
12497      * @cfg {String} invalidText The error text to display if {@link #validator} test fails during validation (defaults to "")
12498      */
12499     invalidText : "",
12500     
12501     
12502     
12503     autocomplete: false,
12504     
12505     
12506     fieldLabel : '',
12507     inputType : 'text',
12508     
12509     name : false,
12510     placeholder: false,
12511     before : false,
12512     after : false,
12513     size : false,
12514     hasFocus : false,
12515     preventMark: false,
12516     isFormField : true,
12517     value : '',
12518     labelWidth : 2,
12519     labelAlign : false,
12520     readOnly : false,
12521     align : false,
12522     formatedValue : false,
12523     forceFeedback : false,
12524     
12525     indicatorpos : 'left',
12526     
12527     labellg : 0,
12528     labelmd : 0,
12529     labelsm : 0,
12530     labelxs : 0,
12531     
12532     capture : '',
12533     accept : '',
12534     
12535     parentLabelAlign : function()
12536     {
12537         var parent = this;
12538         while (parent.parent()) {
12539             parent = parent.parent();
12540             if (typeof(parent.labelAlign) !='undefined') {
12541                 return parent.labelAlign;
12542             }
12543         }
12544         return 'left';
12545         
12546     },
12547     
12548     getAutoCreate : function()
12549     {
12550         
12551         var id = Roo.id();
12552         
12553         var cfg = {};
12554         
12555         if(this.inputType != 'hidden'){
12556             cfg.cls = 'form-group' //input-group
12557         }
12558         
12559         var input =  {
12560             tag: 'input',
12561             id : id,
12562             type : this.inputType,
12563             value : this.value,
12564             cls : 'form-control',
12565             placeholder : this.placeholder || '',
12566             autocomplete : this.autocomplete || 'new-password'
12567         };
12568         if (this.inputType == 'file') {
12569             input.style = 'overflow:hidden'; // why not in CSS?
12570         }
12571         
12572         if(this.capture.length){
12573             input.capture = this.capture;
12574         }
12575         
12576         if(this.accept.length){
12577             input.accept = this.accept + "/*";
12578         }
12579         
12580         if(this.align){
12581             input.style = (typeof(input.style) == 'undefined') ? ('text-align:' + this.align) : (input.style + 'text-align:' + this.align);
12582         }
12583         
12584         if(this.maxLength && this.maxLength != Number.MAX_VALUE){
12585             input.maxLength = this.maxLength;
12586         }
12587         
12588         if (this.disabled) {
12589             input.disabled=true;
12590         }
12591         
12592         if (this.readOnly) {
12593             input.readonly=true;
12594         }
12595         
12596         if (this.name) {
12597             input.name = this.name;
12598         }
12599         
12600         if (this.size) {
12601             input.cls += ' input-' + this.size;
12602         }
12603         
12604         var settings=this;
12605         ['xs','sm','md','lg'].map(function(size){
12606             if (settings[size]) {
12607                 cfg.cls += ' col-' + size + '-' + settings[size];
12608             }
12609         });
12610         
12611         var inputblock = input;
12612         
12613         var feedback = {
12614             tag: 'span',
12615             cls: 'glyphicon form-control-feedback'
12616         };
12617             
12618         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
12619             
12620             inputblock = {
12621                 cls : 'has-feedback',
12622                 cn :  [
12623                     input,
12624                     feedback
12625                 ] 
12626             };  
12627         }
12628         
12629         if (this.before || this.after) {
12630             
12631             inputblock = {
12632                 cls : 'input-group',
12633                 cn :  [] 
12634             };
12635             
12636             if (this.before && typeof(this.before) == 'string') {
12637                 
12638                 inputblock.cn.push({
12639                     tag :'span',
12640                     cls : 'roo-input-before input-group-addon input-group-prepend input-group-text',
12641                     html : this.before
12642                 });
12643             }
12644             if (this.before && typeof(this.before) == 'object') {
12645                 this.before = Roo.factory(this.before);
12646                 
12647                 inputblock.cn.push({
12648                     tag :'span',
12649                     cls : 'roo-input-before input-group-prepend   input-group-' +
12650                         (this.before.xtype == 'Button' ? 'btn' : 'addon')  //?? what about checkboxes - that looks like a bit of a hack thought? 
12651                 });
12652             }
12653             
12654             inputblock.cn.push(input);
12655             
12656             if (this.after && typeof(this.after) == 'string') {
12657                 inputblock.cn.push({
12658                     tag :'span',
12659                     cls : 'roo-input-after input-group-append input-group-text input-group-addon',
12660                     html : this.after
12661                 });
12662             }
12663             if (this.after && typeof(this.after) == 'object') {
12664                 this.after = Roo.factory(this.after);
12665                 
12666                 inputblock.cn.push({
12667                     tag :'span',
12668                     cls : 'roo-input-after input-group-append  input-group-' +
12669                         (this.after.xtype == 'Button' ? 'btn' : 'addon')  //?? what about checkboxes - that looks like a bit of a hack thought? 
12670                 });
12671             }
12672             
12673             if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
12674                 inputblock.cls += ' has-feedback';
12675                 inputblock.cn.push(feedback);
12676             }
12677         };
12678         
12679         
12680         
12681         cfg = this.getAutoCreateLabel( cfg, inputblock );
12682         
12683        
12684          
12685         
12686         if (this.parentType === 'Navbar' &&  this.parent().bar) {
12687            cfg.cls += ' navbar-form';
12688         }
12689         
12690         if (this.parentType === 'NavGroup' && !(Roo.bootstrap.version == 4 && this.parent().form)) {
12691             // on BS4 we do this only if not form 
12692             cfg.cls += ' navbar-form';
12693             cfg.tag = 'li';
12694         }
12695         
12696         return cfg;
12697         
12698     },
12699     /**
12700      * autocreate the label - also used by textara... ?? and others?
12701      */
12702     getAutoCreateLabel : function( cfg, inputblock )
12703     {
12704         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
12705        
12706         var indicator = {
12707             tag : 'i',
12708             cls : 'roo-required-indicator ' + (this.indicatorpos == 'right'  ? 'right' : 'left') +'-indicator text-danger fa fa-lg fa-star',
12709             tooltip : 'This field is required'
12710         };
12711         if (this.allowBlank ) {
12712             indicator.style = this.allowBlank ? ' display:none' : '';
12713         }
12714         if (align ==='left' && this.fieldLabel.length) {
12715             
12716             cfg.cls += ' roo-form-group-label-left'  + (Roo.bootstrap.version == 4 ? ' row' : '');
12717             
12718             cfg.cn = [
12719                 indicator,
12720                 {
12721                     tag: 'label',
12722                     'for' :  id,
12723                     cls : 'control-label col-form-label',
12724                     html : this.fieldLabel
12725
12726                 },
12727                 {
12728                     cls : "", 
12729                     cn: [
12730                         inputblock
12731                     ]
12732                 }
12733             ];
12734             
12735             var labelCfg = cfg.cn[1];
12736             var contentCfg = cfg.cn[2];
12737             
12738             if(this.indicatorpos == 'right'){
12739                 cfg.cn = [
12740                     {
12741                         tag: 'label',
12742                         'for' :  id,
12743                         cls : 'control-label col-form-label',
12744                         cn : [
12745                             {
12746                                 tag : 'span',
12747                                 html : this.fieldLabel
12748                             },
12749                             indicator
12750                         ]
12751                     },
12752                     {
12753                         cls : "",
12754                         cn: [
12755                             inputblock
12756                         ]
12757                     }
12758
12759                 ];
12760                 
12761                 labelCfg = cfg.cn[0];
12762                 contentCfg = cfg.cn[1];
12763             
12764             }
12765             
12766             if(this.labelWidth > 12){
12767                 labelCfg.style = "width: " + this.labelWidth + 'px';
12768             }
12769             
12770             if(this.labelWidth < 13 && this.labelmd == 0){
12771                 this.labellg = this.labellg > 0 ? this.labellg : this.labelWidth;
12772             }
12773             
12774             if(this.labellg > 0){
12775                 labelCfg.cls += ' col-lg-' + this.labellg;
12776                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
12777             }
12778             
12779             if(this.labelmd > 0){
12780                 labelCfg.cls += ' col-md-' + this.labelmd;
12781                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
12782             }
12783             
12784             if(this.labelsm > 0){
12785                 labelCfg.cls += ' col-sm-' + this.labelsm;
12786                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
12787             }
12788             
12789             if(this.labelxs > 0){
12790                 labelCfg.cls += ' col-xs-' + this.labelxs;
12791                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
12792             }
12793             
12794             
12795         } else if ( this.fieldLabel.length) {
12796                 
12797             
12798             
12799             cfg.cn = [
12800                 {
12801                     tag : 'i',
12802                     cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
12803                     tooltip : 'This field is required',
12804                     style : this.allowBlank ? ' display:none' : '' 
12805                 },
12806                 {
12807                     tag: 'label',
12808                    //cls : 'input-group-addon',
12809                     html : this.fieldLabel
12810
12811                 },
12812
12813                inputblock
12814
12815            ];
12816            
12817            if(this.indicatorpos == 'right'){
12818        
12819                 cfg.cn = [
12820                     {
12821                         tag: 'label',
12822                        //cls : 'input-group-addon',
12823                         html : this.fieldLabel
12824
12825                     },
12826                     {
12827                         tag : 'i',
12828                         cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
12829                         tooltip : 'This field is required',
12830                         style : this.allowBlank ? ' display:none' : '' 
12831                     },
12832
12833                    inputblock
12834
12835                ];
12836
12837             }
12838
12839         } else {
12840             
12841             cfg.cn = [
12842
12843                     inputblock
12844
12845             ];
12846                 
12847                 
12848         };
12849         return cfg;
12850     },
12851     
12852     
12853     /**
12854      * return the real input element.
12855      */
12856     inputEl: function ()
12857     {
12858         return this.el.select('input.form-control',true).first();
12859     },
12860     
12861     tooltipEl : function()
12862     {
12863         return this.inputEl();
12864     },
12865     
12866     indicatorEl : function()
12867     {
12868         if (Roo.bootstrap.version == 4) {
12869             return false; // not enabled in v4 yet.
12870         }
12871         
12872         var indicator = this.el.select('i.roo-required-indicator',true).first();
12873         
12874         if(!indicator){
12875             return false;
12876         }
12877         
12878         return indicator;
12879         
12880     },
12881     
12882     setDisabled : function(v)
12883     {
12884         var i  = this.inputEl().dom;
12885         if (!v) {
12886             i.removeAttribute('disabled');
12887             return;
12888             
12889         }
12890         i.setAttribute('disabled','true');
12891     },
12892     initEvents : function()
12893     {
12894           
12895         this.inputEl().on("keydown" , this.fireKey,  this);
12896         this.inputEl().on("focus", this.onFocus,  this);
12897         this.inputEl().on("blur", this.onBlur,  this);
12898         
12899         this.inputEl().relayEvent('keyup', this);
12900         this.inputEl().relayEvent('paste', this);
12901         
12902         this.indicator = this.indicatorEl();
12903         
12904         if(this.indicator){
12905             this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible'); // changed from invisible??? - 
12906         }
12907  
12908         // reference to original value for reset
12909         this.originalValue = this.getValue();
12910         //Roo.form.TextField.superclass.initEvents.call(this);
12911         if(this.validationEvent == 'keyup'){
12912             this.validationTask = new Roo.util.DelayedTask(this.validate, this);
12913             this.inputEl().on('keyup', this.filterValidation, this);
12914         }
12915         else if(this.validationEvent !== false){
12916             this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
12917         }
12918         
12919         if(this.selectOnFocus){
12920             this.on("focus", this.preFocus, this);
12921             
12922         }
12923         if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
12924             this.inputEl().on("keypress", this.filterKeys, this);
12925         } else {
12926             this.inputEl().relayEvent('keypress', this);
12927         }
12928        /* if(this.grow){
12929             this.el.on("keyup", this.onKeyUp,  this, {buffer:50});
12930             this.el.on("click", this.autoSize,  this);
12931         }
12932         */
12933         if(this.inputEl().is('input[type=password]') && Roo.isSafari){
12934             this.inputEl().on('keydown', this.SafariOnKeyDown, this);
12935         }
12936         
12937         if (typeof(this.before) == 'object') {
12938             this.before.render(this.el.select('.roo-input-before',true).first());
12939         }
12940         if (typeof(this.after) == 'object') {
12941             this.after.render(this.el.select('.roo-input-after',true).first());
12942         }
12943         
12944         this.inputEl().on('change', this.onChange, this);
12945         
12946     },
12947     filterValidation : function(e){
12948         if(!e.isNavKeyPress()){
12949             this.validationTask.delay(this.validationDelay);
12950         }
12951     },
12952      /**
12953      * Validates the field value
12954      * @return {Boolean} True if the value is valid, else false
12955      */
12956     validate : function(){
12957         //if(this.disabled || this.validateValue(this.processValue(this.getRawValue()))){
12958         if(this.disabled || this.validateValue(this.getRawValue())){
12959             this.markValid();
12960             return true;
12961         }
12962         
12963         this.markInvalid();
12964         return false;
12965     },
12966     
12967     
12968     /**
12969      * Validates a value according to the field's validation rules and marks the field as invalid
12970      * if the validation fails
12971      * @param {Mixed} value The value to validate
12972      * @return {Boolean} True if the value is valid, else false
12973      */
12974     validateValue : function(value)
12975     {
12976         if(this.getVisibilityEl().hasClass('hidden')){
12977             return true;
12978         }
12979         
12980         if(value.length < 1)  { // if it's blank
12981             if(this.allowBlank){
12982                 return true;
12983             }
12984             return false;
12985         }
12986         
12987         if(value.length < this.minLength){
12988             return false;
12989         }
12990         if(value.length > this.maxLength){
12991             return false;
12992         }
12993         if(this.vtype){
12994             var vt = Roo.form.VTypes;
12995             if(!vt[this.vtype](value, this)){
12996                 return false;
12997             }
12998         }
12999         if(typeof this.validator == "function"){
13000             var msg = this.validator(value);
13001             if (typeof(msg) == 'string') {
13002                 this.invalidText = msg;
13003             }
13004             if(msg !== true){
13005                 return false;
13006             }
13007         }
13008         
13009         if(this.regex && !this.regex.test(value)){
13010             return false;
13011         }
13012         
13013         return true;
13014     },
13015     
13016      // private
13017     fireKey : function(e){
13018         //Roo.log('field ' + e.getKey());
13019         if(e.isNavKeyPress()){
13020             this.fireEvent("specialkey", this, e);
13021         }
13022     },
13023     focus : function (selectText){
13024         if(this.rendered){
13025             this.inputEl().focus();
13026             if(selectText === true){
13027                 this.inputEl().dom.select();
13028             }
13029         }
13030         return this;
13031     } ,
13032     
13033     onFocus : function(){
13034         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
13035            // this.el.addClass(this.focusClass);
13036         }
13037         if(!this.hasFocus){
13038             this.hasFocus = true;
13039             this.startValue = this.getValue();
13040             this.fireEvent("focus", this);
13041         }
13042     },
13043     
13044     beforeBlur : Roo.emptyFn,
13045
13046     
13047     // private
13048     onBlur : function(){
13049         this.beforeBlur();
13050         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
13051             //this.el.removeClass(this.focusClass);
13052         }
13053         this.hasFocus = false;
13054         if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
13055             this.validate();
13056         }
13057         var v = this.getValue();
13058         if(String(v) !== String(this.startValue)){
13059             this.fireEvent('change', this, v, this.startValue);
13060         }
13061         this.fireEvent("blur", this);
13062     },
13063     
13064     onChange : function(e)
13065     {
13066         var v = this.getValue();
13067         if(String(v) !== String(this.startValue)){
13068             this.fireEvent('change', this, v, this.startValue);
13069         }
13070         
13071     },
13072     
13073     /**
13074      * Resets the current field value to the originally loaded value and clears any validation messages
13075      */
13076     reset : function(){
13077         this.setValue(this.originalValue);
13078         this.validate();
13079     },
13080      /**
13081      * Returns the name of the field
13082      * @return {Mixed} name The name field
13083      */
13084     getName: function(){
13085         return this.name;
13086     },
13087      /**
13088      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
13089      * @return {Mixed} value The field value
13090      */
13091     getValue : function(){
13092         var v = this.inputEl().getValue();
13093         return v;
13094     },
13095     /**
13096      * Returns the raw data value which may or may not be a valid, defined value.  To return a normalized value see {@link #getValue}.
13097      * @return {Mixed} value The field value
13098      */
13099     getRawValue : function(){
13100         var v = this.inputEl().getValue();
13101         
13102         return v;
13103     },
13104     
13105     /**
13106      * Sets the underlying DOM field's value directly, bypassing validation.  To set the value with validation see {@link #setValue}.
13107      * @param {Mixed} value The value to set
13108      */
13109     setRawValue : function(v){
13110         return this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
13111     },
13112     
13113     selectText : function(start, end){
13114         var v = this.getRawValue();
13115         if(v.length > 0){
13116             start = start === undefined ? 0 : start;
13117             end = end === undefined ? v.length : end;
13118             var d = this.inputEl().dom;
13119             if(d.setSelectionRange){
13120                 d.setSelectionRange(start, end);
13121             }else if(d.createTextRange){
13122                 var range = d.createTextRange();
13123                 range.moveStart("character", start);
13124                 range.moveEnd("character", v.length-end);
13125                 range.select();
13126             }
13127         }
13128     },
13129     
13130     /**
13131      * Sets a data value into the field and validates it.  To set the value directly without validation see {@link #setRawValue}.
13132      * @param {Mixed} value The value to set
13133      */
13134     setValue : function(v){
13135         this.value = v;
13136         if(this.rendered){
13137             this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
13138             this.validate();
13139         }
13140     },
13141     
13142     /*
13143     processValue : function(value){
13144         if(this.stripCharsRe){
13145             var newValue = value.replace(this.stripCharsRe, '');
13146             if(newValue !== value){
13147                 this.setRawValue(newValue);
13148                 return newValue;
13149             }
13150         }
13151         return value;
13152     },
13153   */
13154     preFocus : function(){
13155         
13156         if(this.selectOnFocus){
13157             this.inputEl().dom.select();
13158         }
13159     },
13160     filterKeys : function(e){
13161         var k = e.getKey();
13162         if(!Roo.isIE && (e.isNavKeyPress() || k == e.BACKSPACE || (k == e.DELETE && e.button == -1))){
13163             return;
13164         }
13165         var c = e.getCharCode(), cc = String.fromCharCode(c);
13166         if(Roo.isIE && (e.isSpecialKey() || !cc)){
13167             return;
13168         }
13169         if(!this.maskRe.test(cc)){
13170             e.stopEvent();
13171         }
13172     },
13173      /**
13174      * Clear any invalid styles/messages for this field
13175      */
13176     clearInvalid : function(){
13177         
13178         if(!this.el || this.preventMark){ // not rendered
13179             return;
13180         }
13181         
13182         
13183         this.el.removeClass([this.invalidClass, 'is-invalid']);
13184         
13185         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
13186             
13187             var feedback = this.el.select('.form-control-feedback', true).first();
13188             
13189             if(feedback){
13190                 this.el.select('.form-control-feedback', true).first().removeClass(this.invalidFeedbackClass);
13191             }
13192             
13193         }
13194         
13195         if(this.indicator){
13196             this.indicator.removeClass('visible');
13197             this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
13198         }
13199         
13200         this.fireEvent('valid', this);
13201     },
13202     
13203      /**
13204      * Mark this field as valid
13205      */
13206     markValid : function()
13207     {
13208         if(!this.el  || this.preventMark){ // not rendered...
13209             return;
13210         }
13211         
13212         this.el.removeClass([this.invalidClass, this.validClass]);
13213         this.inputEl().removeClass(['is-valid', 'is-invalid']);
13214
13215         var feedback = this.el.select('.form-control-feedback', true).first();
13216             
13217         if(feedback){
13218             this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
13219         }
13220         
13221         if(this.indicator){
13222             this.indicator.removeClass('visible');
13223             this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
13224         }
13225         
13226         if(this.disabled){
13227             return;
13228         }
13229         
13230            
13231         if(this.allowBlank && !this.getRawValue().length){
13232             return;
13233         }
13234         if (Roo.bootstrap.version == 3) {
13235             this.el.addClass(this.validClass);
13236         } else {
13237             this.inputEl().addClass('is-valid');
13238         }
13239
13240         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank && (this.getValue().length || this.forceFeedback)){
13241             
13242             var feedback = this.el.select('.form-control-feedback', true).first();
13243             
13244             if(feedback){
13245                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
13246                 this.el.select('.form-control-feedback', true).first().addClass([this.validFeedbackClass]);
13247             }
13248             
13249         }
13250         
13251         this.fireEvent('valid', this);
13252     },
13253     
13254      /**
13255      * Mark this field as invalid
13256      * @param {String} msg The validation message
13257      */
13258     markInvalid : function(msg)
13259     {
13260         if(!this.el  || this.preventMark){ // not rendered
13261             return;
13262         }
13263         
13264         this.el.removeClass([this.invalidClass, this.validClass]);
13265         this.inputEl().removeClass(['is-valid', 'is-invalid']);
13266         
13267         var feedback = this.el.select('.form-control-feedback', true).first();
13268             
13269         if(feedback){
13270             this.el.select('.form-control-feedback', true).first().removeClass(
13271                     [this.invalidFeedbackClass, this.validFeedbackClass]);
13272         }
13273
13274         if(this.disabled){
13275             return;
13276         }
13277         
13278         if(this.allowBlank && !this.getRawValue().length){
13279             return;
13280         }
13281         
13282         if(this.indicator){
13283             this.indicator.removeClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
13284             this.indicator.addClass('visible');
13285         }
13286         if (Roo.bootstrap.version == 3) {
13287             this.el.addClass(this.invalidClass);
13288         } else {
13289             this.inputEl().addClass('is-invalid');
13290         }
13291         
13292         
13293         
13294         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
13295             
13296             var feedback = this.el.select('.form-control-feedback', true).first();
13297             
13298             if(feedback){
13299                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
13300                 
13301                 if(this.getValue().length || this.forceFeedback){
13302                     this.el.select('.form-control-feedback', true).first().addClass([this.invalidFeedbackClass]);
13303                 }
13304                 
13305             }
13306             
13307         }
13308         
13309         this.fireEvent('invalid', this, msg);
13310     },
13311     // private
13312     SafariOnKeyDown : function(event)
13313     {
13314         // this is a workaround for a password hang bug on chrome/ webkit.
13315         if (this.inputEl().dom.type != 'password') {
13316             return;
13317         }
13318         
13319         var isSelectAll = false;
13320         
13321         if(this.inputEl().dom.selectionEnd > 0){
13322             isSelectAll = (this.inputEl().dom.selectionEnd - this.inputEl().dom.selectionStart - this.getValue().length == 0) ? true : false;
13323         }
13324         if(((event.getKey() == 8 || event.getKey() == 46) && this.getValue().length ==1)){ // backspace and delete key
13325             event.preventDefault();
13326             this.setValue('');
13327             return;
13328         }
13329         
13330         if(isSelectAll  && event.getCharCode() > 31 && !event.ctrlKey) { // not backspace and delete key (or ctrl-v)
13331             
13332             event.preventDefault();
13333             // this is very hacky as keydown always get's upper case.
13334             //
13335             var cc = String.fromCharCode(event.getCharCode());
13336             this.setValue( event.shiftKey ?  cc : cc.toLowerCase());
13337             
13338         }
13339     },
13340     adjustWidth : function(tag, w){
13341         tag = tag.toLowerCase();
13342         if(typeof w == 'number' && Roo.isStrict && !Roo.isSafari){
13343             if(Roo.isIE && (tag == 'input' || tag == 'textarea')){
13344                 if(tag == 'input'){
13345                     return w + 2;
13346                 }
13347                 if(tag == 'textarea'){
13348                     return w-2;
13349                 }
13350             }else if(Roo.isOpera){
13351                 if(tag == 'input'){
13352                     return w + 2;
13353                 }
13354                 if(tag == 'textarea'){
13355                     return w-2;
13356                 }
13357             }
13358         }
13359         return w;
13360     },
13361     
13362     setFieldLabel : function(v)
13363     {
13364         if(!this.rendered){
13365             return;
13366         }
13367         
13368         if(this.indicatorEl()){
13369             var ar = this.el.select('label > span',true);
13370             
13371             if (ar.elements.length) {
13372                 this.el.select('label > span',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
13373                 this.fieldLabel = v;
13374                 return;
13375             }
13376             
13377             var br = this.el.select('label',true);
13378             
13379             if(br.elements.length) {
13380                 this.el.select('label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
13381                 this.fieldLabel = v;
13382                 return;
13383             }
13384             
13385             Roo.log('Cannot Found any of label > span || label in input');
13386             return;
13387         }
13388         
13389         this.el.select('label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
13390         this.fieldLabel = v;
13391         
13392         
13393     }
13394 });
13395
13396  
13397 /*
13398  * - LGPL
13399  *
13400  * Input
13401  * 
13402  */
13403
13404 /**
13405  * @class Roo.bootstrap.form.TextArea
13406  * @extends Roo.bootstrap.form.Input
13407  * Bootstrap TextArea class
13408  * @cfg {Number} cols Specifies the visible width of a text area
13409  * @cfg {Number} rows Specifies the visible number of lines in a text area
13410  * @cfg {string} wrap (soft|hard)Specifies how the text in a text area is to be wrapped when submitted in a form
13411  * @cfg {string} resize (none|both|horizontal|vertical|inherit|initial)
13412  * @cfg {string} html text
13413  * 
13414  * @constructor
13415  * Create a new TextArea
13416  * @param {Object} config The config object
13417  */
13418
13419 Roo.bootstrap.form.TextArea = function(config){
13420     Roo.bootstrap.form.TextArea.superclass.constructor.call(this, config);
13421    
13422 };
13423
13424 Roo.extend(Roo.bootstrap.form.TextArea, Roo.bootstrap.form.Input,  {
13425      
13426     cols : false,
13427     rows : 5,
13428     readOnly : false,
13429     warp : 'soft',
13430     resize : false,
13431     value: false,
13432     html: false,
13433     
13434     getAutoCreate : function(){
13435         
13436         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
13437         
13438         var id = Roo.id();
13439         
13440         var cfg = {};
13441         
13442         if(this.inputType != 'hidden'){
13443             cfg.cls = 'form-group' //input-group
13444         }
13445         
13446         var input =  {
13447             tag: 'textarea',
13448             id : id,
13449             warp : this.warp,
13450             rows : this.rows,
13451             value : this.value || '',
13452             html: this.html || '',
13453             cls : 'form-control',
13454             placeholder : this.placeholder || '' 
13455             
13456         };
13457         
13458         if(this.maxLength && this.maxLength != Number.MAX_VALUE){
13459             input.maxLength = this.maxLength;
13460         }
13461         
13462         if(this.resize){
13463             input.style = (typeof(input.style) == 'undefined') ? 'resize:' + this.resize : input.style + 'resize:' + this.resize;
13464         }
13465         
13466         if(this.cols){
13467             input.cols = this.cols;
13468         }
13469         
13470         if (this.readOnly) {
13471             input.readonly = true;
13472         }
13473         
13474         if (this.name) {
13475             input.name = this.name;
13476         }
13477         
13478         if (this.size) {
13479             input.cls = (typeof(input.cls) == 'undefined') ? 'input-' + this.size : input.cls + ' input-' + this.size;
13480         }
13481         
13482         var settings=this;
13483         ['xs','sm','md','lg'].map(function(size){
13484             if (settings[size]) {
13485                 cfg.cls += ' col-' + size + '-' + settings[size];
13486             }
13487         });
13488         
13489         var inputblock = input;
13490         
13491         if(this.hasFeedback && !this.allowBlank){
13492             
13493             var feedback = {
13494                 tag: 'span',
13495                 cls: 'glyphicon form-control-feedback'
13496             };
13497
13498             inputblock = {
13499                 cls : 'has-feedback',
13500                 cn :  [
13501                     input,
13502                     feedback
13503                 ] 
13504             };  
13505         }
13506         
13507         
13508         if (this.before || this.after) {
13509             
13510             inputblock = {
13511                 cls : 'input-group',
13512                 cn :  [] 
13513             };
13514             if (this.before) {
13515                 inputblock.cn.push({
13516                     tag :'span',
13517                     cls : 'input-group-addon',
13518                     html : this.before
13519                 });
13520             }
13521             
13522             inputblock.cn.push(input);
13523             
13524             if(this.hasFeedback && !this.allowBlank){
13525                 inputblock.cls += ' has-feedback';
13526                 inputblock.cn.push(feedback);
13527             }
13528             
13529             if (this.after) {
13530                 inputblock.cn.push({
13531                     tag :'span',
13532                     cls : 'input-group-addon',
13533                     html : this.after
13534                 });
13535             }
13536             
13537         }
13538         
13539         
13540         cfg = this.getAutoCreateLabel( cfg, inputblock );
13541
13542          
13543         
13544         if (this.disabled) {
13545             input.disabled=true;
13546         }
13547         
13548         return cfg;
13549         
13550     },
13551     /**
13552      * return the real textarea element.
13553      */
13554     inputEl: function ()
13555     {
13556         return this.el.select('textarea.form-control',true).first();
13557     },
13558     
13559     /**
13560      * Clear any invalid styles/messages for this field
13561      */
13562     clearInvalid : function()
13563     {
13564         
13565         if(!this.el || this.preventMark){ // not rendered
13566             return;
13567         }
13568         
13569         var label = this.el.select('label', true).first();
13570         //var icon = this.el.select('i.fa-star', true).first();
13571         
13572         //if(label && icon){
13573         //    icon.remove();
13574         //}
13575         this.el.removeClass( this.validClass);
13576         this.inputEl().removeClass('is-invalid');
13577          
13578         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
13579             
13580             var feedback = this.el.select('.form-control-feedback', true).first();
13581             
13582             if(feedback){
13583                 this.el.select('.form-control-feedback', true).first().removeClass(this.invalidFeedbackClass);
13584             }
13585             
13586         }
13587         
13588         this.fireEvent('valid', this);
13589     },
13590     
13591      /**
13592      * Mark this field as valid
13593      */
13594     markValid : function()
13595     {
13596         if(!this.el  || this.preventMark){ // not rendered
13597             return;
13598         }
13599         
13600         this.el.removeClass([this.invalidClass, this.validClass]);
13601         this.inputEl().removeClass(['is-valid', 'is-invalid']);
13602         
13603         var feedback = this.el.select('.form-control-feedback', true).first();
13604             
13605         if(feedback){
13606             this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
13607         }
13608
13609         if(this.disabled || this.allowBlank){
13610             return;
13611         }
13612         
13613         var label = this.el.select('label', true).first();
13614         var icon = this.el.select('i.fa-star', true).first();
13615         
13616         //if(label && icon){
13617         //    icon.remove();
13618         //}
13619         if (Roo.bootstrap.version == 3) {
13620             this.el.addClass(this.validClass);
13621         } else {
13622             this.inputEl().addClass('is-valid');
13623         }
13624         
13625         
13626         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank && (this.getValue().length || this.forceFeedback)){
13627             
13628             var feedback = this.el.select('.form-control-feedback', true).first();
13629             
13630             if(feedback){
13631                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
13632                 this.el.select('.form-control-feedback', true).first().addClass([this.validFeedbackClass]);
13633             }
13634             
13635         }
13636         
13637         this.fireEvent('valid', this);
13638     },
13639     
13640      /**
13641      * Mark this field as invalid
13642      * @param {String} msg The validation message
13643      */
13644     markInvalid : function(msg)
13645     {
13646         if(!this.el  || this.preventMark){ // not rendered
13647             return;
13648         }
13649         
13650         this.el.removeClass([this.invalidClass, this.validClass]);
13651         this.inputEl().removeClass(['is-valid', 'is-invalid']);
13652         
13653         var feedback = this.el.select('.form-control-feedback', true).first();
13654             
13655         if(feedback){
13656             this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
13657         }
13658
13659         if(this.disabled){
13660             return;
13661         }
13662         
13663         var label = this.el.select('label', true).first();
13664         //var icon = this.el.select('i.fa-star', true).first();
13665         
13666         //if(!this.getValue().length && label && !icon){
13667           /*  this.el.createChild({
13668                 tag : 'i',
13669                 cls : 'text-danger fa fa-lg fa-star',
13670                 tooltip : 'This field is required',
13671                 style : 'margin-right:5px;'
13672             }, label, true);
13673             */
13674         //}
13675         
13676         if (Roo.bootstrap.version == 3) {
13677             this.el.addClass(this.invalidClass);
13678         } else {
13679             this.inputEl().addClass('is-invalid');
13680         }
13681         
13682         // fixme ... this may be depricated need to test..
13683         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
13684             
13685             var feedback = this.el.select('.form-control-feedback', true).first();
13686             
13687             if(feedback){
13688                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
13689                 
13690                 if(this.getValue().length || this.forceFeedback){
13691                     this.el.select('.form-control-feedback', true).first().addClass([this.invalidFeedbackClass]);
13692                 }
13693                 
13694             }
13695             
13696         }
13697         
13698         this.fireEvent('invalid', this, msg);
13699     }
13700 });
13701
13702  
13703 /*
13704  * - LGPL
13705  *
13706  * trigger field - base class for combo..
13707  * 
13708  */
13709  
13710 /**
13711  * @class Roo.bootstrap.form.TriggerField
13712  * @extends Roo.bootstrap.form.Input
13713  * Provides a convenient wrapper for TextFields that adds a clickable trigger button (looks like a combobox by default).
13714  * The trigger has no default action, so you must assign a function to implement the trigger click handler by
13715  * overriding {@link #onTriggerClick}. You can create a TriggerField directly, as it renders exactly like a combobox
13716  * for which you can provide a custom implementation.  For example:
13717  * <pre><code>
13718 var trigger = new Roo.bootstrap.form.TriggerField();
13719 trigger.onTriggerClick = myTriggerFn;
13720 trigger.applyTo('my-field');
13721 </code></pre>
13722  *
13723  * However, in general you will most likely want to use TriggerField as the base class for a reusable component.
13724  * {@link Roo.bootstrap.form.DateField} and {@link Roo.bootstrap.form.ComboBox} are perfect examples of this.
13725  * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
13726  * class 'x-form-trigger' by default and triggerClass will be <b>appended</b> if specified.
13727  * @cfg {String} caret (search|calendar) BS3 only - carat fa name
13728
13729  * @constructor
13730  * Create a new TriggerField.
13731  * @param {Object} config Configuration options (valid {@Roo.bootstrap.form.Input} config options will also be applied
13732  * to the base TextField)
13733  */
13734 Roo.bootstrap.form.TriggerField = function(config){
13735     this.mimicing = false;
13736     Roo.bootstrap.form.TriggerField.superclass.constructor.call(this, config);
13737 };
13738
13739 Roo.extend(Roo.bootstrap.form.TriggerField, Roo.bootstrap.form.Input,  {
13740     /**
13741      * @cfg {String} triggerClass A CSS class to apply to the trigger
13742      */
13743      /**
13744      * @cfg {Boolean} hideTrigger True to hide the trigger element and display only the base text field (defaults to false)
13745      */
13746     hideTrigger:false,
13747
13748     /**
13749      * @cfg {Boolean} removable (true|false) special filter default false
13750      */
13751     removable : false,
13752     
13753     /** @cfg {Boolean} grow @hide */
13754     /** @cfg {Number} growMin @hide */
13755     /** @cfg {Number} growMax @hide */
13756
13757     /**
13758      * @hide 
13759      * @method
13760      */
13761     autoSize: Roo.emptyFn,
13762     // private
13763     monitorTab : true,
13764     // private
13765     deferHeight : true,
13766
13767     
13768     actionMode : 'wrap',
13769     
13770     caret : false,
13771     
13772     
13773     getAutoCreate : function(){
13774        
13775         var align = this.labelAlign || this.parentLabelAlign();
13776         
13777         var id = Roo.id();
13778         
13779         var cfg = {
13780             cls: 'form-group' //input-group
13781         };
13782         
13783         
13784         var input =  {
13785             tag: 'input',
13786             id : id,
13787             type : this.inputType,
13788             cls : 'form-control',
13789             autocomplete: 'new-password',
13790             placeholder : this.placeholder || '' 
13791             
13792         };
13793         if (this.name) {
13794             input.name = this.name;
13795         }
13796         if (this.size) {
13797             input.cls += ' input-' + this.size;
13798         }
13799         
13800         if (this.disabled) {
13801             input.disabled=true;
13802         }
13803         
13804         var inputblock = input;
13805         
13806         if(this.hasFeedback && !this.allowBlank){
13807             
13808             var feedback = {
13809                 tag: 'span',
13810                 cls: 'glyphicon form-control-feedback'
13811             };
13812             
13813             if(this.removable && !this.editable  ){
13814                 inputblock = {
13815                     cls : 'has-feedback',
13816                     cn :  [
13817                         inputblock,
13818                         {
13819                             tag: 'button',
13820                             html : 'x',
13821                             cls : 'roo-combo-removable-btn close'
13822                         },
13823                         feedback
13824                     ] 
13825                 };
13826             } else {
13827                 inputblock = {
13828                     cls : 'has-feedback',
13829                     cn :  [
13830                         inputblock,
13831                         feedback
13832                     ] 
13833                 };
13834             }
13835
13836         } else {
13837             if(this.removable && !this.editable ){
13838                 inputblock = {
13839                     cls : 'roo-removable',
13840                     cn :  [
13841                         inputblock,
13842                         {
13843                             tag: 'button',
13844                             html : 'x',
13845                             cls : 'roo-combo-removable-btn close'
13846                         }
13847                     ] 
13848                 };
13849             }
13850         }
13851         
13852         if (this.before || this.after) {
13853             
13854             inputblock = {
13855                 cls : 'input-group',
13856                 cn :  [] 
13857             };
13858             if (this.before) {
13859                 inputblock.cn.push({
13860                     tag :'span',
13861                     cls : 'input-group-addon input-group-prepend input-group-text',
13862                     html : this.before
13863                 });
13864             }
13865             
13866             inputblock.cn.push(input);
13867             
13868             if(this.hasFeedback && !this.allowBlank){
13869                 inputblock.cls += ' has-feedback';
13870                 inputblock.cn.push(feedback);
13871             }
13872             
13873             if (this.after) {
13874                 inputblock.cn.push({
13875                     tag :'span',
13876                     cls : 'input-group-addon input-group-append input-group-text',
13877                     html : this.after
13878                 });
13879             }
13880             
13881         };
13882         
13883       
13884         
13885         var ibwrap = inputblock;
13886         
13887         if(this.multiple){
13888             ibwrap = {
13889                 tag: 'ul',
13890                 cls: 'roo-select2-choices',
13891                 cn:[
13892                     {
13893                         tag: 'li',
13894                         cls: 'roo-select2-search-field',
13895                         cn: [
13896
13897                             inputblock
13898                         ]
13899                     }
13900                 ]
13901             };
13902                 
13903         }
13904         
13905         var combobox = {
13906             cls: 'roo-select2-container input-group',
13907             cn: [
13908                  {
13909                     tag: 'input',
13910                     type : 'hidden',
13911                     cls: 'form-hidden-field'
13912                 },
13913                 ibwrap
13914             ]
13915         };
13916         
13917         if(!this.multiple && this.showToggleBtn){
13918             
13919             var caret = {
13920                         tag: 'span',
13921                         cls: 'caret'
13922              };
13923             if (this.caret != false) {
13924                 caret = {
13925                      tag: 'i',
13926                      cls: 'fa fa-' + this.caret
13927                 };
13928                 
13929             }
13930             
13931             combobox.cn.push({
13932                 tag :'span',
13933                 cls : 'input-group-addon input-group-append input-group-text btn dropdown-toggle',
13934                 cn : [
13935                     Roo.bootstrap.version == 3 ? caret : '',
13936                     {
13937                         tag: 'span',
13938                         cls: 'combobox-clear',
13939                         cn  : [
13940                             {
13941                                 tag : 'i',
13942                                 cls: 'icon-remove'
13943                             }
13944                         ]
13945                     }
13946                 ]
13947
13948             })
13949         }
13950         
13951         if(this.multiple){
13952             combobox.cls += ' roo-select2-container-multi';
13953         }
13954          var indicator = {
13955             tag : 'i',
13956             cls : 'roo-required-indicator ' + (this.indicatorpos == 'right'  ? 'right' : 'left') +'-indicator text-danger fa fa-lg fa-star',
13957             tooltip : 'This field is required'
13958         };
13959       
13960         if (this.allowBlank) {
13961             indicator = {
13962                 tag : 'i',
13963                 style : 'display:none'
13964             };
13965         }
13966          
13967         
13968         
13969         if (align ==='left' && this.fieldLabel.length) {
13970             
13971             cfg.cls += ' roo-form-group-label-left'  + (Roo.bootstrap.version == 4 ? ' row' : '');
13972
13973             cfg.cn = [
13974                 indicator,
13975                 {
13976                     tag: 'label',
13977                     'for' :  id,
13978                     cls : 'control-label',
13979                     html : this.fieldLabel
13980
13981                 },
13982                 {
13983                     cls : "", 
13984                     cn: [
13985                         combobox
13986                     ]
13987                 }
13988
13989             ];
13990             
13991             var labelCfg = cfg.cn[1];
13992             var contentCfg = cfg.cn[2];
13993             
13994             if(this.indicatorpos == 'right'){
13995                 cfg.cn = [
13996                     {
13997                         tag: 'label',
13998                         'for' :  id,
13999                         cls : 'control-label',
14000                         cn : [
14001                             {
14002                                 tag : 'span',
14003                                 html : this.fieldLabel
14004                             },
14005                             indicator
14006                         ]
14007                     },
14008                     {
14009                         cls : "", 
14010                         cn: [
14011                             combobox
14012                         ]
14013                     }
14014
14015                 ];
14016                 
14017                 labelCfg = cfg.cn[0];
14018                 contentCfg = cfg.cn[1];
14019             }
14020             
14021             if(this.labelWidth > 12){
14022                 labelCfg.style = "width: " + this.labelWidth + 'px';
14023             }
14024             
14025             if(this.labelWidth < 13 && this.labelmd == 0){
14026                 this.labelmd = this.labelWidth;
14027             }
14028             
14029             if(this.labellg > 0){
14030                 labelCfg.cls += ' col-lg-' + this.labellg;
14031                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
14032             }
14033             
14034             if(this.labelmd > 0){
14035                 labelCfg.cls += ' col-md-' + this.labelmd;
14036                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
14037             }
14038             
14039             if(this.labelsm > 0){
14040                 labelCfg.cls += ' col-sm-' + this.labelsm;
14041                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
14042             }
14043             
14044             if(this.labelxs > 0){
14045                 labelCfg.cls += ' col-xs-' + this.labelxs;
14046                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
14047             }
14048             
14049         } else if ( this.fieldLabel.length) {
14050 //                Roo.log(" label");
14051             cfg.cn = [
14052                 indicator,
14053                {
14054                    tag: 'label',
14055                    //cls : 'input-group-addon',
14056                    html : this.fieldLabel
14057
14058                },
14059
14060                combobox
14061
14062             ];
14063             
14064             if(this.indicatorpos == 'right'){
14065                 
14066                 cfg.cn = [
14067                     {
14068                        tag: 'label',
14069                        cn : [
14070                            {
14071                                tag : 'span',
14072                                html : this.fieldLabel
14073                            },
14074                            indicator
14075                        ]
14076
14077                     },
14078                     combobox
14079
14080                 ];
14081
14082             }
14083
14084         } else {
14085             
14086 //                Roo.log(" no label && no align");
14087                 cfg = combobox
14088                      
14089                 
14090         }
14091         
14092         var settings=this;
14093         ['xs','sm','md','lg'].map(function(size){
14094             if (settings[size]) {
14095                 cfg.cls += ' col-' + size + '-' + settings[size];
14096             }
14097         });
14098         
14099         return cfg;
14100         
14101     },
14102     
14103     
14104     
14105     // private
14106     onResize : function(w, h){
14107 //        Roo.bootstrap.form.TriggerField.superclass.onResize.apply(this, arguments);
14108 //        if(typeof w == 'number'){
14109 //            var x = w - this.trigger.getWidth();
14110 //            this.inputEl().setWidth(this.adjustWidth('input', x));
14111 //            this.trigger.setStyle('left', x+'px');
14112 //        }
14113     },
14114
14115     // private
14116     adjustSize : Roo.BoxComponent.prototype.adjustSize,
14117
14118     // private
14119     getResizeEl : function(){
14120         return this.inputEl();
14121     },
14122
14123     // private
14124     getPositionEl : function(){
14125         return this.inputEl();
14126     },
14127
14128     // private
14129     alignErrorIcon : function(){
14130         this.errorIcon.alignTo(this.inputEl(), 'tl-tr', [2, 0]);
14131     },
14132
14133     // private
14134     initEvents : function(){
14135         
14136         this.createList();
14137         
14138         Roo.bootstrap.form.TriggerField.superclass.initEvents.call(this);
14139         //this.wrap = this.el.wrap({cls: "x-form-field-wrap"});
14140         if(!this.multiple && this.showToggleBtn){
14141             this.trigger = this.el.select('span.dropdown-toggle',true).first();
14142             if(this.hideTrigger){
14143                 this.trigger.setDisplayed(false);
14144             }
14145             this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
14146         }
14147         
14148         if(this.multiple){
14149             this.inputEl().on("click", this.onTriggerClick, this, {preventDefault:true});
14150         }
14151         
14152         if(this.removable && !this.editable && !this.tickable){
14153             var close = this.closeTriggerEl();
14154             
14155             if(close){
14156                 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
14157                 close.on('click', this.removeBtnClick, this, close);
14158             }
14159         }
14160         
14161         //this.trigger.addClassOnOver('x-form-trigger-over');
14162         //this.trigger.addClassOnClick('x-form-trigger-click');
14163         
14164         //if(!this.width){
14165         //    this.wrap.setWidth(this.el.getWidth()+this.trigger.getWidth());
14166         //}
14167     },
14168     
14169     closeTriggerEl : function()
14170     {
14171         var close = this.el.select('.roo-combo-removable-btn', true).first();
14172         return close ? close : false;
14173     },
14174     
14175     removeBtnClick : function(e, h, el)
14176     {
14177         e.preventDefault();
14178         
14179         if(this.fireEvent("remove", this) !== false){
14180             this.reset();
14181             this.fireEvent("afterremove", this)
14182         }
14183     },
14184     
14185     createList : function()
14186     {
14187         this.list = Roo.get(document.body).createChild({
14188             tag: Roo.bootstrap.version == 4 ? 'div' : 'ul',
14189             cls: 'typeahead typeahead-long dropdown-menu shadow',
14190             style: 'display:none'
14191         });
14192         
14193         this.list.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';;
14194         
14195     },
14196
14197     // private
14198     initTrigger : function(){
14199        
14200     },
14201
14202     // private
14203     onDestroy : function(){
14204         if(this.trigger){
14205             this.trigger.removeAllListeners();
14206           //  this.trigger.remove();
14207         }
14208         //if(this.wrap){
14209         //    this.wrap.remove();
14210         //}
14211         Roo.bootstrap.form.TriggerField.superclass.onDestroy.call(this);
14212     },
14213
14214     // private
14215     onFocus : function(){
14216         Roo.bootstrap.form.TriggerField.superclass.onFocus.call(this);
14217         /*
14218         if(!this.mimicing){
14219             this.wrap.addClass('x-trigger-wrap-focus');
14220             this.mimicing = true;
14221             Roo.get(Roo.isIE ? document.body : document).on("mousedown", this.mimicBlur, this);
14222             if(this.monitorTab){
14223                 this.el.on("keydown", this.checkTab, this);
14224             }
14225         }
14226         */
14227     },
14228
14229     // private
14230     checkTab : function(e){
14231         if(e.getKey() == e.TAB){
14232             this.triggerBlur();
14233         }
14234     },
14235
14236     // private
14237     onBlur : function(){
14238         // do nothing
14239     },
14240
14241     // private
14242     mimicBlur : function(e, t){
14243         /*
14244         if(!this.wrap.contains(t) && this.validateBlur()){
14245             this.triggerBlur();
14246         }
14247         */
14248     },
14249
14250     // private
14251     triggerBlur : function(){
14252         this.mimicing = false;
14253         Roo.get(Roo.isIE ? document.body : document).un("mousedown", this.mimicBlur);
14254         if(this.monitorTab){
14255             this.el.un("keydown", this.checkTab, this);
14256         }
14257         //this.wrap.removeClass('x-trigger-wrap-focus');
14258         Roo.bootstrap.form.TriggerField.superclass.onBlur.call(this);
14259     },
14260
14261     // private
14262     // This should be overriden by any subclass that needs to check whether or not the field can be blurred.
14263     validateBlur : function(e, t){
14264         return true;
14265     },
14266
14267     // private
14268     onDisable : function(){
14269         this.inputEl().dom.disabled = true;
14270         //Roo.bootstrap.form.TriggerField.superclass.onDisable.call(this);
14271         //if(this.wrap){
14272         //    this.wrap.addClass('x-item-disabled');
14273         //}
14274     },
14275
14276     // private
14277     onEnable : function(){
14278         this.inputEl().dom.disabled = false;
14279         //Roo.bootstrap.form.TriggerField.superclass.onEnable.call(this);
14280         //if(this.wrap){
14281         //    this.el.removeClass('x-item-disabled');
14282         //}
14283     },
14284
14285     // private
14286     onShow : function(){
14287         var ae = this.getActionEl();
14288         
14289         if(ae){
14290             ae.dom.style.display = '';
14291             ae.dom.style.visibility = 'visible';
14292         }
14293     },
14294
14295     // private
14296     
14297     onHide : function(){
14298         var ae = this.getActionEl();
14299         ae.dom.style.display = 'none';
14300     },
14301
14302     /**
14303      * The function that should handle the trigger's click event.  This method does nothing by default until overridden
14304      * by an implementing function.
14305      * @method
14306      * @param {EventObject} e
14307      */
14308     onTriggerClick : Roo.emptyFn
14309 });
14310  
14311 /*
14312 * Licence: LGPL
14313 */
14314
14315 /**
14316  * @class Roo.bootstrap.form.CardUploader
14317  * @extends Roo.bootstrap.Button
14318  * Bootstrap Card Uploader class - it's a button which when you add files to it, adds cards below with preview and the name...
14319  * @cfg {Number} errorTimeout default 3000
14320  * @cfg {Array}  images  an array of ?? Img objects ??? when loading existing files..
14321  * @cfg {Array}  html The button text.
14322
14323  *
14324  * @constructor
14325  * Create a new CardUploader
14326  * @param {Object} config The config object
14327  */
14328
14329 Roo.bootstrap.form.CardUploader = function(config){
14330     
14331  
14332     
14333     Roo.bootstrap.form.CardUploader.superclass.constructor.call(this, config);
14334     
14335     
14336     this.fileCollection   = new Roo.util.MixedCollection(false,function(r) {
14337         return r.data.id
14338      });
14339     
14340      this.addEvents({
14341          // raw events
14342         /**
14343          * @event preview
14344          * When a image is clicked on - and needs to display a slideshow or similar..
14345          * @param {Roo.bootstrap.Card} this
14346          * @param {Object} The image information data 
14347          *
14348          */
14349         'preview' : true,
14350          /**
14351          * @event download
14352          * When a the download link is clicked
14353          * @param {Roo.bootstrap.Card} this
14354          * @param {Object} The image information data  contains 
14355          */
14356         'download' : true
14357         
14358     });
14359 };
14360  
14361 Roo.extend(Roo.bootstrap.form.CardUploader, Roo.bootstrap.form.Input,  {
14362     
14363      
14364     errorTimeout : 3000,
14365      
14366     images : false,
14367    
14368     fileCollection : false,
14369     allowBlank : true,
14370     
14371     getAutoCreate : function()
14372     {
14373         
14374         var cfg =  {
14375             cls :'form-group' ,
14376             cn : [
14377                
14378                 {
14379                     tag: 'label',
14380                    //cls : 'input-group-addon',
14381                     html : this.fieldLabel
14382
14383                 },
14384
14385                 {
14386                     tag: 'input',
14387                     type : 'hidden',
14388                     name : this.name,
14389                     value : this.value,
14390                     cls : 'd-none  form-control'
14391                 },
14392                 
14393                 {
14394                     tag: 'input',
14395                     multiple : 'multiple',
14396                     type : 'file',
14397                     cls : 'd-none  roo-card-upload-selector'
14398                 },
14399                 
14400                 {
14401                     cls : 'roo-card-uploader-button-container w-100 mb-2'
14402                 },
14403                 {
14404                     cls : 'card-columns roo-card-uploader-container'
14405                 }
14406
14407             ]
14408         };
14409            
14410          
14411         return cfg;
14412     },
14413     
14414     getChildContainer : function() /// what children are added to.
14415     {
14416         return this.containerEl;
14417     },
14418    
14419     getButtonContainer : function() /// what children are added to.
14420     {
14421         return this.el.select(".roo-card-uploader-button-container").first();
14422     },
14423    
14424     initEvents : function()
14425     {
14426         
14427         Roo.bootstrap.form.Input.prototype.initEvents.call(this);
14428         
14429         var t = this;
14430         this.addxtype({
14431             xns: Roo.bootstrap,
14432
14433             xtype : 'Button',
14434             container_method : 'getButtonContainer' ,            
14435             html :  this.html, // fix changable?
14436             cls : 'w-100 ',
14437             listeners : {
14438                 'click' : function(btn, e) {
14439                     t.onClick(e);
14440                 }
14441             }
14442         });
14443         
14444         
14445         
14446         
14447         this.urlAPI = (window.createObjectURL && window) || 
14448                                 (window.URL && URL.revokeObjectURL && URL) || 
14449                                 (window.webkitURL && webkitURL);
14450                         
14451          
14452          
14453          
14454         this.selectorEl = this.el.select('.roo-card-upload-selector', true).first();
14455         
14456         this.selectorEl.on('change', this.onFileSelected, this);
14457         if (this.images) {
14458             var t = this;
14459             this.images.forEach(function(img) {
14460                 t.addCard(img)
14461             });
14462             this.images = false;
14463         }
14464         this.containerEl = this.el.select('.roo-card-uploader-container', true).first();
14465          
14466        
14467     },
14468     
14469    
14470     onClick : function(e)
14471     {
14472         e.preventDefault();
14473          
14474         this.selectorEl.dom.click();
14475          
14476     },
14477     
14478     onFileSelected : function(e)
14479     {
14480         e.preventDefault();
14481         
14482         if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
14483             return;
14484         }
14485         
14486         Roo.each(this.selectorEl.dom.files, function(file){    
14487             this.addFile(file);
14488         }, this);
14489          
14490     },
14491     
14492       
14493     
14494       
14495     
14496     addFile : function(file)
14497     {
14498            
14499         if(typeof(file) === 'string'){
14500             throw "Add file by name?"; // should not happen
14501             return;
14502         }
14503         
14504         if(!file || !this.urlAPI){
14505             return;
14506         }
14507         
14508         // file;
14509         // file.type;
14510         
14511         var _this = this;
14512         
14513         
14514         var url = _this.urlAPI.createObjectURL( file);
14515            
14516         this.addCard({
14517             id : Roo.bootstrap.form.CardUploader.ID--,
14518             is_uploaded : false,
14519             src : url,
14520             srcfile : file,
14521             title : file.name,
14522             mimetype : file.type,
14523             preview : false,
14524             is_deleted : 0
14525         });
14526         
14527     },
14528     
14529     /**
14530      * addCard - add an Attachment to the uploader
14531      * @param data - the data about the image to upload
14532      *
14533      * {
14534           id : 123
14535           title : "Title of file",
14536           is_uploaded : false,
14537           src : "http://.....",
14538           srcfile : { the File upload object },
14539           mimetype : file.type,
14540           preview : false,
14541           is_deleted : 0
14542           .. any other data...
14543         }
14544      *
14545      * 
14546     */
14547     
14548     addCard : function (data)
14549     {
14550         // hidden input element?
14551         // if the file is not an image...
14552         //then we need to use something other that and header_image
14553         var t = this;
14554         //   remove.....
14555         var footer = [
14556             {
14557                 xns : Roo.bootstrap,
14558                 xtype : 'CardFooter',
14559                  items: [
14560                     {
14561                         xns : Roo.bootstrap,
14562                         xtype : 'Element',
14563                         cls : 'd-flex',
14564                         items : [
14565                             
14566                             {
14567                                 xns : Roo.bootstrap,
14568                                 xtype : 'Button',
14569                                 html : String.format("<small>{0}</small>", data.title),
14570                                 cls : 'col-10 text-left',
14571                                 size: 'sm',
14572                                 weight: 'link',
14573                                 fa : 'download',
14574                                 listeners : {
14575                                     click : function() {
14576                                      
14577                                         t.fireEvent( "download", t, data );
14578                                     }
14579                                 }
14580                             },
14581                           
14582                             {
14583                                 xns : Roo.bootstrap,
14584                                 xtype : 'Button',
14585                                 style: 'max-height: 28px; ',
14586                                 size : 'sm',
14587                                 weight: 'danger',
14588                                 cls : 'col-2',
14589                                 fa : 'times',
14590                                 listeners : {
14591                                     click : function() {
14592                                         t.removeCard(data.id)
14593                                     }
14594                                 }
14595                             }
14596                         ]
14597                     }
14598                     
14599                 ] 
14600             }
14601             
14602         ];
14603         
14604         var cn = this.addxtype(
14605             {
14606                  
14607                 xns : Roo.bootstrap,
14608                 xtype : 'Card',
14609                 closeable : true,
14610                 header : !data.mimetype.match(/image/) && !data.preview ? "Document": false,
14611                 header_image : data.mimetype.match(/image/) ? data.src  : data.preview,
14612                 header_image_fit_square: true, // fixme  - we probably need to use the 'Img' element to do stuff like this.
14613                 data : data,
14614                 html : false,
14615                  
14616                 items : footer,
14617                 initEvents : function() {
14618                     Roo.bootstrap.Card.prototype.initEvents.call(this);
14619                     var card = this;
14620                     this.imgEl = this.el.select('.card-img-top').first();
14621                     if (this.imgEl) {
14622                         this.imgEl.on('click', function() { t.fireEvent( "preview", t, data ); }, this);
14623                         this.imgEl.set({ 'pointer' : 'cursor' });
14624                                   
14625                     }
14626                     this.getCardFooter().addClass('p-1');
14627                     
14628                   
14629                 }
14630                 
14631             }
14632         );
14633         // dont' really need ot update items.
14634         // this.items.push(cn);
14635         this.fileCollection.add(cn);
14636         
14637         if (!data.srcfile) {
14638             this.updateInput();
14639             return;
14640         }
14641             
14642         var _t = this;
14643         var reader = new FileReader();
14644         reader.addEventListener("load", function() {  
14645             data.srcdata =  reader.result;
14646             _t.updateInput();
14647         });
14648         reader.readAsDataURL(data.srcfile);
14649         
14650         
14651         
14652     },
14653     removeCard : function(id)
14654     {
14655         
14656         var card  = this.fileCollection.get(id);
14657         card.data.is_deleted = 1;
14658         card.data.src = ''; /// delete the source - so it reduces size of not uploaded images etc.
14659         //this.fileCollection.remove(card);
14660         //this.items = this.items.filter(function(e) { return e != card });
14661         // dont' really need ot update items.
14662         card.el.dom.parentNode.removeChild(card.el.dom);
14663         this.updateInput();
14664
14665         
14666     },
14667     reset: function()
14668     {
14669         this.fileCollection.each(function(card) {
14670             if (card.el.dom && card.el.dom.parentNode) {
14671                 card.el.dom.parentNode.removeChild(card.el.dom);
14672             }
14673         });
14674         this.fileCollection.clear();
14675         this.updateInput();
14676     },
14677     
14678     updateInput : function()
14679     {
14680          var data = [];
14681         this.fileCollection.each(function(e) {
14682             data.push(e.data);
14683             
14684         });
14685         this.inputEl().dom.value = JSON.stringify(data);
14686         
14687         
14688         
14689     }
14690     
14691     
14692 });
14693
14694
14695 Roo.bootstrap.form.CardUploader.ID = -1;/*
14696  * Based on:
14697  * Ext JS Library 1.1.1
14698  * Copyright(c) 2006-2007, Ext JS, LLC.
14699  *
14700  * Originally Released Under LGPL - original licence link has changed is not relivant.
14701  *
14702  * Fork - LGPL
14703  * <script type="text/javascript">
14704  */
14705
14706
14707 /**
14708  * @class Roo.data.SortTypes
14709  * @static
14710  * Defines the default sorting (casting?) comparison functions used when sorting data.
14711  */
14712 Roo.data.SortTypes = {
14713     /**
14714      * Default sort that does nothing
14715      * @param {Mixed} s The value being converted
14716      * @return {Mixed} The comparison value
14717      */
14718     none : function(s){
14719         return s;
14720     },
14721     
14722     /**
14723      * The regular expression used to strip tags
14724      * @type {RegExp}
14725      * @property
14726      */
14727     stripTagsRE : /<\/?[^>]+>/gi,
14728     
14729     /**
14730      * Strips all HTML tags to sort on text only
14731      * @param {Mixed} s The value being converted
14732      * @return {String} The comparison value
14733      */
14734     asText : function(s){
14735         return String(s).replace(this.stripTagsRE, "");
14736     },
14737     
14738     /**
14739      * Strips all HTML tags to sort on text only - Case insensitive
14740      * @param {Mixed} s The value being converted
14741      * @return {String} The comparison value
14742      */
14743     asUCText : function(s){
14744         return String(s).toUpperCase().replace(this.stripTagsRE, "");
14745     },
14746     
14747     /**
14748      * Case insensitive string
14749      * @param {Mixed} s The value being converted
14750      * @return {String} The comparison value
14751      */
14752     asUCString : function(s) {
14753         return String(s).toUpperCase();
14754     },
14755     
14756     /**
14757      * Date sorting
14758      * @param {Mixed} s The value being converted
14759      * @return {Number} The comparison value
14760      */
14761     asDate : function(s) {
14762         if(!s){
14763             return 0;
14764         }
14765         if(s instanceof Date){
14766             return s.getTime();
14767         }
14768         return Date.parse(String(s));
14769     },
14770     
14771     /**
14772      * Float sorting
14773      * @param {Mixed} s The value being converted
14774      * @return {Float} The comparison value
14775      */
14776     asFloat : function(s) {
14777         var val = parseFloat(String(s).replace(/,/g, ""));
14778         if(isNaN(val)) {
14779             val = 0;
14780         }
14781         return val;
14782     },
14783     
14784     /**
14785      * Integer sorting
14786      * @param {Mixed} s The value being converted
14787      * @return {Number} The comparison value
14788      */
14789     asInt : function(s) {
14790         var val = parseInt(String(s).replace(/,/g, ""));
14791         if(isNaN(val)) {
14792             val = 0;
14793         }
14794         return val;
14795     }
14796 };/*
14797  * Based on:
14798  * Ext JS Library 1.1.1
14799  * Copyright(c) 2006-2007, Ext JS, LLC.
14800  *
14801  * Originally Released Under LGPL - original licence link has changed is not relivant.
14802  *
14803  * Fork - LGPL
14804  * <script type="text/javascript">
14805  */
14806
14807 /**
14808 * @class Roo.data.Record
14809  * Instances of this class encapsulate both record <em>definition</em> information, and record
14810  * <em>value</em> information for use in {@link Roo.data.Store} objects, or any code which needs
14811  * to access Records cached in an {@link Roo.data.Store} object.<br>
14812  * <p>
14813  * Constructors for this class are generated by passing an Array of field definition objects to {@link #create}.
14814  * Instances are usually only created by {@link Roo.data.Reader} implementations when processing unformatted data
14815  * objects.<br>
14816  * <p>
14817  * Record objects generated by this constructor inherit all the methods of Roo.data.Record listed below.
14818  * @constructor
14819  * This constructor should not be used to create Record objects. Instead, use the constructor generated by
14820  * {@link #create}. The parameters are the same.
14821  * @param {Array} data An associative Array of data values keyed by the field name.
14822  * @param {Object} id (Optional) The id of the record. This id should be unique, and is used by the
14823  * {@link Roo.data.Store} object which owns the Record to index its collection of Records. If
14824  * not specified an integer id is generated.
14825  */
14826 Roo.data.Record = function(data, id){
14827     this.id = (id || id === 0) ? id : ++Roo.data.Record.AUTO_ID;
14828     this.data = data;
14829 };
14830
14831 /**
14832  * Generate a constructor for a specific record layout.
14833  * @param {Array} o An Array of field definition objects which specify field names, and optionally,
14834  * data types, and a mapping for an {@link Roo.data.Reader} to extract the field's value from a data object.
14835  * Each field definition object may contain the following properties: <ul>
14836  * <li><b>name</b> : String<p style="margin-left:1em">The name by which the field is referenced within the Record. This is referenced by,
14837  * for example the <em>dataIndex</em> property in column definition objects passed to {@link Roo.grid.ColumnModel}</p></li>
14838  * <li><b>mapping</b> : String<p style="margin-left:1em">(Optional) A path specification for use by the {@link Roo.data.Reader} implementation
14839  * that is creating the Record to access the data value from the data object. If an {@link Roo.data.JsonReader}
14840  * is being used, then this is a string containing the javascript expression to reference the data relative to 
14841  * the record item's root. If an {@link Roo.data.XmlReader} is being used, this is an {@link Roo.DomQuery} path
14842  * to the data item relative to the record element. If the mapping expression is the same as the field name,
14843  * this may be omitted.</p></li>
14844  * <li><b>type</b> : String<p style="margin-left:1em">(Optional) The data type for conversion to displayable value. Possible values are
14845  * <ul><li>auto (Default, implies no conversion)</li>
14846  * <li>string</li>
14847  * <li>int</li>
14848  * <li>float</li>
14849  * <li>boolean</li>
14850  * <li>date</li></ul></p></li>
14851  * <li><b>sortType</b> : Mixed<p style="margin-left:1em">(Optional) A member of {@link Roo.data.SortTypes}.</p></li>
14852  * <li><b>sortDir</b> : String<p style="margin-left:1em">(Optional) Initial direction to sort. "ASC" or "DESC"</p></li>
14853  * <li><b>convert</b> : Function<p style="margin-left:1em">(Optional) A function which converts the value provided
14854  * by the Reader into an object that will be stored in the Record. It is passed the
14855  * following parameters:<ul>
14856  * <li><b>v</b> : Mixed<p style="margin-left:1em">The data value as read by the Reader.</p></li>
14857  * </ul></p></li>
14858  * <li><b>dateFormat</b> : String<p style="margin-left:1em">(Optional) A format String for the Date.parseDate function.</p></li>
14859  * </ul>
14860  * <br>usage:<br><pre><code>
14861 var TopicRecord = Roo.data.Record.create(
14862     {name: 'title', mapping: 'topic_title'},
14863     {name: 'author', mapping: 'username'},
14864     {name: 'totalPosts', mapping: 'topic_replies', type: 'int'},
14865     {name: 'lastPost', mapping: 'post_time', type: 'date'},
14866     {name: 'lastPoster', mapping: 'user2'},
14867     {name: 'excerpt', mapping: 'post_text'}
14868 );
14869
14870 var myNewRecord = new TopicRecord({
14871     title: 'Do my job please',
14872     author: 'noobie',
14873     totalPosts: 1,
14874     lastPost: new Date(),
14875     lastPoster: 'Animal',
14876     excerpt: 'No way dude!'
14877 });
14878 myStore.add(myNewRecord);
14879 </code></pre>
14880  * @method create
14881  * @static
14882  */
14883 Roo.data.Record.create = function(o){
14884     var f = function(){
14885         f.superclass.constructor.apply(this, arguments);
14886     };
14887     Roo.extend(f, Roo.data.Record);
14888     var p = f.prototype;
14889     p.fields = new Roo.util.MixedCollection(false, function(field){
14890         return field.name;
14891     });
14892     for(var i = 0, len = o.length; i < len; i++){
14893         p.fields.add(new Roo.data.Field(o[i]));
14894     }
14895     f.getField = function(name){
14896         return p.fields.get(name);  
14897     };
14898     return f;
14899 };
14900
14901 Roo.data.Record.AUTO_ID = 1000;
14902 Roo.data.Record.EDIT = 'edit';
14903 Roo.data.Record.REJECT = 'reject';
14904 Roo.data.Record.COMMIT = 'commit';
14905
14906 Roo.data.Record.prototype = {
14907     /**
14908      * Readonly flag - true if this record has been modified.
14909      * @type Boolean
14910      */
14911     dirty : false,
14912     editing : false,
14913     error: null,
14914     modified: null,
14915
14916     // private
14917     join : function(store){
14918         this.store = store;
14919     },
14920
14921     /**
14922      * Set the named field to the specified value.
14923      * @param {String} name The name of the field to set.
14924      * @param {Object} value The value to set the field to.
14925      */
14926     set : function(name, value){
14927         if(this.data[name] == value){
14928             return;
14929         }
14930         this.dirty = true;
14931         if(!this.modified){
14932             this.modified = {};
14933         }
14934         if(typeof this.modified[name] == 'undefined'){
14935             this.modified[name] = this.data[name];
14936         }
14937         this.data[name] = value;
14938         if(!this.editing && this.store){
14939             this.store.afterEdit(this);
14940         }       
14941     },
14942
14943     /**
14944      * Get the value of the named field.
14945      * @param {String} name The name of the field to get the value of.
14946      * @return {Object} The value of the field.
14947      */
14948     get : function(name){
14949         return this.data[name]; 
14950     },
14951
14952     // private
14953     beginEdit : function(){
14954         this.editing = true;
14955         this.modified = {}; 
14956     },
14957
14958     // private
14959     cancelEdit : function(){
14960         this.editing = false;
14961         delete this.modified;
14962     },
14963
14964     // private
14965     endEdit : function(){
14966         this.editing = false;
14967         if(this.dirty && this.store){
14968             this.store.afterEdit(this);
14969         }
14970     },
14971
14972     /**
14973      * Usually called by the {@link Roo.data.Store} which owns the Record.
14974      * Rejects all changes made to the Record since either creation, or the last commit operation.
14975      * Modified fields are reverted to their original values.
14976      * <p>
14977      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
14978      * of reject operations.
14979      */
14980     reject : function(){
14981         var m = this.modified;
14982         for(var n in m){
14983             if(typeof m[n] != "function"){
14984                 this.data[n] = m[n];
14985             }
14986         }
14987         this.dirty = false;
14988         delete this.modified;
14989         this.editing = false;
14990         if(this.store){
14991             this.store.afterReject(this);
14992         }
14993     },
14994
14995     /**
14996      * Usually called by the {@link Roo.data.Store} which owns the Record.
14997      * Commits all changes made to the Record since either creation, or the last commit operation.
14998      * <p>
14999      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
15000      * of commit operations.
15001      */
15002     commit : function(){
15003         this.dirty = false;
15004         delete this.modified;
15005         this.editing = false;
15006         if(this.store){
15007             this.store.afterCommit(this);
15008         }
15009     },
15010
15011     // private
15012     hasError : function(){
15013         return this.error != null;
15014     },
15015
15016     // private
15017     clearError : function(){
15018         this.error = null;
15019     },
15020
15021     /**
15022      * Creates a copy of this record.
15023      * @param {String} id (optional) A new record id if you don't want to use this record's id
15024      * @return {Record}
15025      */
15026     copy : function(newId) {
15027         return new this.constructor(Roo.apply({}, this.data), newId || this.id);
15028     }
15029 };/*
15030  * Based on:
15031  * Ext JS Library 1.1.1
15032  * Copyright(c) 2006-2007, Ext JS, LLC.
15033  *
15034  * Originally Released Under LGPL - original licence link has changed is not relivant.
15035  *
15036  * Fork - LGPL
15037  * <script type="text/javascript">
15038  */
15039
15040
15041
15042 /**
15043  * @class Roo.data.Store
15044  * @extends Roo.util.Observable
15045  * The Store class encapsulates a client side cache of {@link Roo.data.Record} objects which provide input data
15046  * for widgets such as the Roo.grid.Grid, or the Roo.form.ComboBox.<br>
15047  * <p>
15048  * A Store object uses an implementation of {@link Roo.data.DataProxy} to access a data object unless you call loadData() directly and pass in your data. The Store object
15049  * has no knowledge of the format of the data returned by the Proxy.<br>
15050  * <p>
15051  * A Store object uses its configured implementation of {@link Roo.data.DataReader} to create {@link Roo.data.Record}
15052  * instances from the data object. These records are cached and made available through accessor functions.
15053  * @constructor
15054  * Creates a new Store.
15055  * @param {Object} config A config object containing the objects needed for the Store to access data,
15056  * and read the data into Records.
15057  */
15058 Roo.data.Store = function(config){
15059     this.data = new Roo.util.MixedCollection(false);
15060     this.data.getKey = function(o){
15061         return o.id;
15062     };
15063     this.baseParams = {};
15064     // private
15065     this.paramNames = {
15066         "start" : "start",
15067         "limit" : "limit",
15068         "sort" : "sort",
15069         "dir" : "dir",
15070         "multisort" : "_multisort"
15071     };
15072
15073     if(config && config.data){
15074         this.inlineData = config.data;
15075         delete config.data;
15076     }
15077
15078     Roo.apply(this, config);
15079     
15080     if(this.reader){ // reader passed
15081         this.reader = Roo.factory(this.reader, Roo.data);
15082         this.reader.xmodule = this.xmodule || false;
15083         if(!this.recordType){
15084             this.recordType = this.reader.recordType;
15085         }
15086         if(this.reader.onMetaChange){
15087             this.reader.onMetaChange = this.onMetaChange.createDelegate(this);
15088         }
15089     }
15090
15091     if(this.recordType){
15092         this.fields = this.recordType.prototype.fields;
15093     }
15094     this.modified = [];
15095
15096     this.addEvents({
15097         /**
15098          * @event datachanged
15099          * Fires when the data cache has changed, and a widget which is using this Store
15100          * as a Record cache should refresh its view.
15101          * @param {Store} this
15102          */
15103         datachanged : true,
15104         /**
15105          * @event metachange
15106          * Fires when this store's reader provides new metadata (fields). This is currently only support for JsonReaders.
15107          * @param {Store} this
15108          * @param {Object} meta The JSON metadata
15109          */
15110         metachange : true,
15111         /**
15112          * @event add
15113          * Fires when Records have been added to the Store
15114          * @param {Store} this
15115          * @param {Roo.data.Record[]} records The array of Records added
15116          * @param {Number} index The index at which the record(s) were added
15117          */
15118         add : true,
15119         /**
15120          * @event remove
15121          * Fires when a Record has been removed from the Store
15122          * @param {Store} this
15123          * @param {Roo.data.Record} record The Record that was removed
15124          * @param {Number} index The index at which the record was removed
15125          */
15126         remove : true,
15127         /**
15128          * @event update
15129          * Fires when a Record has been updated
15130          * @param {Store} this
15131          * @param {Roo.data.Record} record The Record that was updated
15132          * @param {String} operation The update operation being performed.  Value may be one of:
15133          * <pre><code>
15134  Roo.data.Record.EDIT
15135  Roo.data.Record.REJECT
15136  Roo.data.Record.COMMIT
15137          * </code></pre>
15138          */
15139         update : true,
15140         /**
15141          * @event clear
15142          * Fires when the data cache has been cleared.
15143          * @param {Store} this
15144          */
15145         clear : true,
15146         /**
15147          * @event beforeload
15148          * Fires before a request is made for a new data object.  If the beforeload handler returns false
15149          * the load action will be canceled.
15150          * @param {Store} this
15151          * @param {Object} options The loading options that were specified (see {@link #load} for details)
15152          */
15153         beforeload : true,
15154         /**
15155          * @event beforeloadadd
15156          * Fires after a new set of Records has been loaded.
15157          * @param {Store} this
15158          * @param {Roo.data.Record[]} records The Records that were loaded
15159          * @param {Object} options The loading options that were specified (see {@link #load} for details)
15160          */
15161         beforeloadadd : true,
15162         /**
15163          * @event load
15164          * Fires after a new set of Records has been loaded, before they are added to the store.
15165          * @param {Store} this
15166          * @param {Roo.data.Record[]} records The Records that were loaded
15167          * @param {Object} options The loading options that were specified (see {@link #load} for details)
15168          * @params {Object} return from reader
15169          */
15170         load : true,
15171         /**
15172          * @event loadexception
15173          * Fires if an exception occurs in the Proxy during loading.
15174          * Called with the signature of the Proxy's "loadexception" event.
15175          * If you return Json { data: [] , success: false, .... } then this will be thrown with the following args
15176          * 
15177          * @param {Proxy} 
15178          * @param {Object} ret return data from JsonData.reader() - success, totalRecords, records
15179          * @param {Object} opts - load Options
15180          * @param {Object} jsonData from your request (normally this contains the Exception)
15181          */
15182         loadexception : true
15183     });
15184     
15185     if(this.proxy){
15186         this.proxy = Roo.factory(this.proxy, Roo.data);
15187         this.proxy.xmodule = this.xmodule || false;
15188         this.relayEvents(this.proxy,  ["loadexception"]);
15189     }
15190     this.sortToggle = {};
15191     this.sortOrder = []; // array of order of sorting - updated by grid if multisort is enabled.
15192
15193     Roo.data.Store.superclass.constructor.call(this);
15194
15195     if(this.inlineData){
15196         this.loadData(this.inlineData);
15197         delete this.inlineData;
15198     }
15199 };
15200
15201 Roo.extend(Roo.data.Store, Roo.util.Observable, {
15202      /**
15203     * @cfg {boolean} isLocal   flag if data is locally available (and can be always looked up
15204     * without a remote query - used by combo/forms at present.
15205     */
15206     
15207     /**
15208     * @cfg {Roo.data.DataProxy} proxy [required] The Proxy object which provides access to a data object.
15209     */
15210     /**
15211     * @cfg {Array} data Inline data to be loaded when the store is initialized.
15212     */
15213     /**
15214     * @cfg {Roo.data.DataReader} reader [required]  The Reader object which processes the data object and returns
15215     * an Array of Roo.data.record objects which are cached keyed by their <em>id</em> property.
15216     */
15217     /**
15218     * @cfg {Object} baseParams An object containing properties which are to be sent as parameters
15219     * on any HTTP request
15220     */
15221     /**
15222     * @cfg {Object} sortInfo A config object in the format: {field: "fieldName", direction: "ASC|DESC"}
15223     */
15224     /**
15225     * @cfg {Boolean} multiSort enable multi column sorting (sort is based on the order of columns, remote only at present)
15226     */
15227     multiSort: false,
15228     /**
15229     * @cfg {boolean} remoteSort True if sorting is to be handled by requesting the Proxy to provide a refreshed
15230     * version of the data object in sorted order, as opposed to sorting the Record cache in place (defaults to false).
15231     */
15232     remoteSort : false,
15233
15234     /**
15235     * @cfg {boolean} pruneModifiedRecords True to clear all modified record information each time the store is
15236      * loaded or when a record is removed. (defaults to false).
15237     */
15238     pruneModifiedRecords : false,
15239
15240     // private
15241     lastOptions : null,
15242
15243     /**
15244      * Add Records to the Store and fires the add event.
15245      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
15246      */
15247     add : function(records){
15248         records = [].concat(records);
15249         for(var i = 0, len = records.length; i < len; i++){
15250             records[i].join(this);
15251         }
15252         var index = this.data.length;
15253         this.data.addAll(records);
15254         this.fireEvent("add", this, records, index);
15255     },
15256
15257     /**
15258      * Remove a Record from the Store and fires the remove event.
15259      * @param {Ext.data.Record} record The Roo.data.Record object to remove from the cache.
15260      */
15261     remove : function(record){
15262         var index = this.data.indexOf(record);
15263         this.data.removeAt(index);
15264  
15265         if(this.pruneModifiedRecords){
15266             this.modified.remove(record);
15267         }
15268         this.fireEvent("remove", this, record, index);
15269     },
15270
15271     /**
15272      * Remove all Records from the Store and fires the clear event.
15273      */
15274     removeAll : function(){
15275         this.data.clear();
15276         if(this.pruneModifiedRecords){
15277             this.modified = [];
15278         }
15279         this.fireEvent("clear", this);
15280     },
15281
15282     /**
15283      * Inserts Records to the Store at the given index and fires the add event.
15284      * @param {Number} index The start index at which to insert the passed Records.
15285      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
15286      */
15287     insert : function(index, records){
15288         records = [].concat(records);
15289         for(var i = 0, len = records.length; i < len; i++){
15290             this.data.insert(index, records[i]);
15291             records[i].join(this);
15292         }
15293         this.fireEvent("add", this, records, index);
15294     },
15295
15296     /**
15297      * Get the index within the cache of the passed Record.
15298      * @param {Roo.data.Record} record The Roo.data.Record object to to find.
15299      * @return {Number} The index of the passed Record. Returns -1 if not found.
15300      */
15301     indexOf : function(record){
15302         return this.data.indexOf(record);
15303     },
15304
15305     /**
15306      * Get the index within the cache of the Record with the passed id.
15307      * @param {String} id The id of the Record to find.
15308      * @return {Number} The index of the Record. Returns -1 if not found.
15309      */
15310     indexOfId : function(id){
15311         return this.data.indexOfKey(id);
15312     },
15313
15314     /**
15315      * Get the Record with the specified id.
15316      * @param {String} id The id of the Record to find.
15317      * @return {Roo.data.Record} The Record with the passed id. Returns undefined if not found.
15318      */
15319     getById : function(id){
15320         return this.data.key(id);
15321     },
15322
15323     /**
15324      * Get the Record at the specified index.
15325      * @param {Number} index The index of the Record to find.
15326      * @return {Roo.data.Record} The Record at the passed index. Returns undefined if not found.
15327      */
15328     getAt : function(index){
15329         return this.data.itemAt(index);
15330     },
15331
15332     /**
15333      * Returns a range of Records between specified indices.
15334      * @param {Number} startIndex (optional) The starting index (defaults to 0)
15335      * @param {Number} endIndex (optional) The ending index (defaults to the last Record in the Store)
15336      * @return {Roo.data.Record[]} An array of Records
15337      */
15338     getRange : function(start, end){
15339         return this.data.getRange(start, end);
15340     },
15341
15342     // private
15343     storeOptions : function(o){
15344         o = Roo.apply({}, o);
15345         delete o.callback;
15346         delete o.scope;
15347         this.lastOptions = o;
15348     },
15349
15350     /**
15351      * Loads the Record cache from the configured Proxy using the configured Reader.
15352      * <p>
15353      * If using remote paging, then the first load call must specify the <em>start</em>
15354      * and <em>limit</em> properties in the options.params property to establish the initial
15355      * position within the dataset, and the number of Records to cache on each read from the Proxy.
15356      * <p>
15357      * <strong>It is important to note that for remote data sources, loading is asynchronous,
15358      * and this call will return before the new data has been loaded. Perform any post-processing
15359      * in a callback function, or in a "load" event handler.</strong>
15360      * <p>
15361      * @param {Object} options An object containing properties which control loading options:<ul>
15362      * <li>params {Object} An object containing properties to pass as HTTP parameters to a remote data source.</li>
15363      * <li>params.data {Object} if you are using a MemoryProxy / JsonReader, use this as the data to load stuff..
15364      * <pre>
15365                 {
15366                     data : data,  // array of key=>value data like JsonReader
15367                     total : data.length,
15368                     success : true
15369                     
15370                 }
15371         </pre>
15372             }.</li>
15373      * <li>callback {Function} A function to be called after the Records have been loaded. The callback is
15374      * passed the following arguments:<ul>
15375      * <li>r : Roo.data.Record[]</li>
15376      * <li>options: Options object from the load call</li>
15377      * <li>success: Boolean success indicator</li></ul></li>
15378      * <li>scope {Object} Scope with which to call the callback (defaults to the Store object)</li>
15379      * <li>add {Boolean} indicator to append loaded records rather than replace the current cache.</li>
15380      * </ul>
15381      */
15382     load : function(options){
15383         options = options || {};
15384         if(this.fireEvent("beforeload", this, options) !== false){
15385             this.storeOptions(options);
15386             var p = Roo.apply(options.params || {}, this.baseParams);
15387             // if meta was not loaded from remote source.. try requesting it.
15388             if (!this.reader.metaFromRemote) {
15389                 p._requestMeta = 1;
15390             }
15391             if(this.sortInfo && this.remoteSort){
15392                 var pn = this.paramNames;
15393                 p[pn["sort"]] = this.sortInfo.field;
15394                 p[pn["dir"]] = this.sortInfo.direction;
15395             }
15396             if (this.multiSort) {
15397                 var pn = this.paramNames;
15398                 p[pn["multisort"]] = Roo.encode( { sort : this.sortToggle, order: this.sortOrder });
15399             }
15400             
15401             this.proxy.load(p, this.reader, this.loadRecords, this, options);
15402         }
15403     },
15404
15405     /**
15406      * Reloads the Record cache from the configured Proxy using the configured Reader and
15407      * the options from the last load operation performed.
15408      * @param {Object} options (optional) An object containing properties which may override the options
15409      * used in the last load operation. See {@link #load} for details (defaults to null, in which case
15410      * the most recently used options are reused).
15411      */
15412     reload : function(options){
15413         this.load(Roo.applyIf(options||{}, this.lastOptions));
15414     },
15415
15416     // private
15417     // Called as a callback by the Reader during a load operation.
15418     loadRecords : function(o, options, success){
15419          
15420         if(!o){
15421             if(success !== false){
15422                 this.fireEvent("load", this, [], options, o);
15423             }
15424             if(options.callback){
15425                 options.callback.call(options.scope || this, [], options, false);
15426             }
15427             return;
15428         }
15429         // if data returned failure - throw an exception.
15430         if (o.success === false) {
15431             // show a message if no listener is registered.
15432             if (!this.hasListener('loadexception') && typeof(o.raw.errorMsg) != 'undefined') {
15433                     Roo.MessageBox.alert("Error loading",o.raw.errorMsg);
15434             }
15435             // loadmask wil be hooked into this..
15436             this.fireEvent("loadexception", this, o, options, o.raw.errorMsg);
15437             return;
15438         }
15439         var r = o.records, t = o.totalRecords || r.length;
15440         
15441         this.fireEvent("beforeloadadd", this, r, options, o);
15442         
15443         if(!options || options.add !== true){
15444             if(this.pruneModifiedRecords){
15445                 this.modified = [];
15446             }
15447             for(var i = 0, len = r.length; i < len; i++){
15448                 r[i].join(this);
15449             }
15450             if(this.snapshot){
15451                 this.data = this.snapshot;
15452                 delete this.snapshot;
15453             }
15454             this.data.clear();
15455             this.data.addAll(r);
15456             this.totalLength = t;
15457             this.applySort();
15458             this.fireEvent("datachanged", this);
15459         }else{
15460             this.totalLength = Math.max(t, this.data.length+r.length);
15461             this.add(r);
15462         }
15463         
15464         if(this.parent && !Roo.isIOS && !this.useNativeIOS && this.parent.emptyTitle.length) {
15465                 
15466             var e = new Roo.data.Record({});
15467
15468             e.set(this.parent.displayField, this.parent.emptyTitle);
15469             e.set(this.parent.valueField, '');
15470
15471             this.insert(0, e);
15472         }
15473             
15474         this.fireEvent("load", this, r, options, o);
15475         if(options.callback){
15476             options.callback.call(options.scope || this, r, options, true);
15477         }
15478     },
15479
15480
15481     /**
15482      * Loads data from a passed data block. A Reader which understands the format of the data
15483      * must have been configured in the constructor.
15484      * @param {Object} data The data block from which to read the Records.  The format of the data expected
15485      * is dependent on the type of Reader that is configured and should correspond to that Reader's readRecords parameter.
15486      * @param {Boolean} append (Optional) True to append the new Records rather than replace the existing cache.
15487      */
15488     loadData : function(o, append){
15489         var r = this.reader.readRecords(o);
15490         this.loadRecords(r, {add: append}, true);
15491     },
15492     
15493      /**
15494      * using 'cn' the nested child reader read the child array into it's child stores.
15495      * @param {Object} rec The record with a 'children array
15496      */
15497     loadDataFromChildren : function(rec)
15498     {
15499         this.loadData(this.reader.toLoadData(rec));
15500     },
15501     
15502
15503     /**
15504      * Gets the number of cached records.
15505      * <p>
15506      * <em>If using paging, this may not be the total size of the dataset. If the data object
15507      * used by the Reader contains the dataset size, then the getTotalCount() function returns
15508      * the data set size</em>
15509      */
15510     getCount : function(){
15511         return this.data.length || 0;
15512     },
15513
15514     /**
15515      * Gets the total number of records in the dataset as returned by the server.
15516      * <p>
15517      * <em>If using paging, for this to be accurate, the data object used by the Reader must contain
15518      * the dataset size</em>
15519      */
15520     getTotalCount : function(){
15521         return this.totalLength || 0;
15522     },
15523
15524     /**
15525      * Returns the sort state of the Store as an object with two properties:
15526      * <pre><code>
15527  field {String} The name of the field by which the Records are sorted
15528  direction {String} The sort order, "ASC" or "DESC"
15529      * </code></pre>
15530      */
15531     getSortState : function(){
15532         return this.sortInfo;
15533     },
15534
15535     // private
15536     applySort : function(){
15537         if(this.sortInfo && !this.remoteSort){
15538             var s = this.sortInfo, f = s.field;
15539             var st = this.fields.get(f).sortType;
15540             var fn = function(r1, r2){
15541                 var v1 = st(r1.data[f]), v2 = st(r2.data[f]);
15542                 return v1 > v2 ? 1 : (v1 < v2 ? -1 : 0);
15543             };
15544             this.data.sort(s.direction, fn);
15545             if(this.snapshot && this.snapshot != this.data){
15546                 this.snapshot.sort(s.direction, fn);
15547             }
15548         }
15549     },
15550
15551     /**
15552      * Sets the default sort column and order to be used by the next load operation.
15553      * @param {String} fieldName The name of the field to sort by.
15554      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
15555      */
15556     setDefaultSort : function(field, dir){
15557         this.sortInfo = {field: field, direction: dir ? dir.toUpperCase() : "ASC"};
15558     },
15559
15560     /**
15561      * Sort the Records.
15562      * If remote sorting is used, the sort is performed on the server, and the cache is
15563      * reloaded. If local sorting is used, the cache is sorted internally.
15564      * @param {String} fieldName The name of the field to sort by.
15565      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
15566      */
15567     sort : function(fieldName, dir){
15568         var f = this.fields.get(fieldName);
15569         if(!dir){
15570             this.sortToggle[f.name] = this.sortToggle[f.name] || f.sortDir;
15571             
15572             if(this.multiSort || (this.sortInfo && this.sortInfo.field == f.name) ){ // toggle sort dir
15573                 dir = (this.sortToggle[f.name] || "ASC").toggle("ASC", "DESC");
15574             }else{
15575                 dir = f.sortDir;
15576             }
15577         }
15578         this.sortToggle[f.name] = dir;
15579         this.sortInfo = {field: f.name, direction: dir};
15580         if(!this.remoteSort){
15581             this.applySort();
15582             this.fireEvent("datachanged", this);
15583         }else{
15584             this.load(this.lastOptions);
15585         }
15586     },
15587
15588     /**
15589      * Calls the specified function for each of the Records in the cache.
15590      * @param {Function} fn The function to call. The Record is passed as the first parameter.
15591      * Returning <em>false</em> aborts and exits the iteration.
15592      * @param {Object} scope (optional) The scope in which to call the function (defaults to the Record).
15593      */
15594     each : function(fn, scope){
15595         this.data.each(fn, scope);
15596     },
15597
15598     /**
15599      * Gets all records modified since the last commit.  Modified records are persisted across load operations
15600      * (e.g., during paging).
15601      * @return {Roo.data.Record[]} An array of Records containing outstanding modifications.
15602      */
15603     getModifiedRecords : function(){
15604         return this.modified;
15605     },
15606
15607     // private
15608     createFilterFn : function(property, value, anyMatch){
15609         if(!value.exec){ // not a regex
15610             value = String(value);
15611             if(value.length == 0){
15612                 return false;
15613             }
15614             value = new RegExp((anyMatch === true ? '' : '^') + Roo.escapeRe(value), "i");
15615         }
15616         return function(r){
15617             return value.test(r.data[property]);
15618         };
15619     },
15620
15621     /**
15622      * Sums the value of <i>property</i> for each record between start and end and returns the result.
15623      * @param {String} property A field on your records
15624      * @param {Number} start The record index to start at (defaults to 0)
15625      * @param {Number} end The last record index to include (defaults to length - 1)
15626      * @return {Number} The sum
15627      */
15628     sum : function(property, start, end){
15629         var rs = this.data.items, v = 0;
15630         start = start || 0;
15631         end = (end || end === 0) ? end : rs.length-1;
15632
15633         for(var i = start; i <= end; i++){
15634             v += (rs[i].data[property] || 0);
15635         }
15636         return v;
15637     },
15638
15639     /**
15640      * Filter the records by a specified property.
15641      * @param {String} field A field on your records
15642      * @param {String/RegExp} value Either a string that the field
15643      * should start with or a RegExp to test against the field
15644      * @param {Boolean} anyMatch True to match any part not just the beginning
15645      */
15646     filter : function(property, value, anyMatch){
15647         var fn = this.createFilterFn(property, value, anyMatch);
15648         return fn ? this.filterBy(fn) : this.clearFilter();
15649     },
15650
15651     /**
15652      * Filter by a function. The specified function will be called with each
15653      * record in this data source. If the function returns true the record is included,
15654      * otherwise it is filtered.
15655      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
15656      * @param {Object} scope (optional) The scope of the function (defaults to this)
15657      */
15658     filterBy : function(fn, scope){
15659         this.snapshot = this.snapshot || this.data;
15660         this.data = this.queryBy(fn, scope||this);
15661         this.fireEvent("datachanged", this);
15662     },
15663
15664     /**
15665      * Query the records by a specified property.
15666      * @param {String} field A field on your records
15667      * @param {String/RegExp} value Either a string that the field
15668      * should start with or a RegExp to test against the field
15669      * @param {Boolean} anyMatch True to match any part not just the beginning
15670      * @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
15671      */
15672     query : function(property, value, anyMatch){
15673         var fn = this.createFilterFn(property, value, anyMatch);
15674         return fn ? this.queryBy(fn) : this.data.clone();
15675     },
15676
15677     /**
15678      * Query by a function. The specified function will be called with each
15679      * record in this data source. If the function returns true the record is included
15680      * in the results.
15681      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
15682      * @param {Object} scope (optional) The scope of the function (defaults to this)
15683       @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
15684      **/
15685     queryBy : function(fn, scope){
15686         var data = this.snapshot || this.data;
15687         return data.filterBy(fn, scope||this);
15688     },
15689
15690     /**
15691      * Collects unique values for a particular dataIndex from this store.
15692      * @param {String} dataIndex The property to collect
15693      * @param {Boolean} allowNull (optional) Pass true to allow null, undefined or empty string values
15694      * @param {Boolean} bypassFilter (optional) Pass true to collect from all records, even ones which are filtered
15695      * @return {Array} An array of the unique values
15696      **/
15697     collect : function(dataIndex, allowNull, bypassFilter){
15698         var d = (bypassFilter === true && this.snapshot) ?
15699                 this.snapshot.items : this.data.items;
15700         var v, sv, r = [], l = {};
15701         for(var i = 0, len = d.length; i < len; i++){
15702             v = d[i].data[dataIndex];
15703             sv = String(v);
15704             if((allowNull || !Roo.isEmpty(v)) && !l[sv]){
15705                 l[sv] = true;
15706                 r[r.length] = v;
15707             }
15708         }
15709         return r;
15710     },
15711
15712     /**
15713      * Revert to a view of the Record cache with no filtering applied.
15714      * @param {Boolean} suppressEvent If true the filter is cleared silently without notifying listeners
15715      */
15716     clearFilter : function(suppressEvent){
15717         if(this.snapshot && this.snapshot != this.data){
15718             this.data = this.snapshot;
15719             delete this.snapshot;
15720             if(suppressEvent !== true){
15721                 this.fireEvent("datachanged", this);
15722             }
15723         }
15724     },
15725
15726     // private
15727     afterEdit : function(record){
15728         if(this.modified.indexOf(record) == -1){
15729             this.modified.push(record);
15730         }
15731         this.fireEvent("update", this, record, Roo.data.Record.EDIT);
15732     },
15733     
15734     // private
15735     afterReject : function(record){
15736         this.modified.remove(record);
15737         this.fireEvent("update", this, record, Roo.data.Record.REJECT);
15738     },
15739
15740     // private
15741     afterCommit : function(record){
15742         this.modified.remove(record);
15743         this.fireEvent("update", this, record, Roo.data.Record.COMMIT);
15744     },
15745
15746     /**
15747      * Commit all Records with outstanding changes. To handle updates for changes, subscribe to the
15748      * Store's "update" event, and perform updating when the third parameter is Roo.data.Record.COMMIT.
15749      */
15750     commitChanges : function(){
15751         var m = this.modified.slice(0);
15752         this.modified = [];
15753         for(var i = 0, len = m.length; i < len; i++){
15754             m[i].commit();
15755         }
15756     },
15757
15758     /**
15759      * Cancel outstanding changes on all changed records.
15760      */
15761     rejectChanges : function(){
15762         var m = this.modified.slice(0);
15763         this.modified = [];
15764         for(var i = 0, len = m.length; i < len; i++){
15765             m[i].reject();
15766         }
15767     },
15768
15769     onMetaChange : function(meta, rtype, o){
15770         this.recordType = rtype;
15771         this.fields = rtype.prototype.fields;
15772         delete this.snapshot;
15773         this.sortInfo = meta.sortInfo || this.sortInfo;
15774         this.modified = [];
15775         this.fireEvent('metachange', this, this.reader.meta);
15776     },
15777     
15778     moveIndex : function(data, type)
15779     {
15780         var index = this.indexOf(data);
15781         
15782         var newIndex = index + type;
15783         
15784         this.remove(data);
15785         
15786         this.insert(newIndex, data);
15787         
15788     }
15789 });/*
15790  * Based on:
15791  * Ext JS Library 1.1.1
15792  * Copyright(c) 2006-2007, Ext JS, LLC.
15793  *
15794  * Originally Released Under LGPL - original licence link has changed is not relivant.
15795  *
15796  * Fork - LGPL
15797  * <script type="text/javascript">
15798  */
15799
15800 /**
15801  * @class Roo.data.SimpleStore
15802  * @extends Roo.data.Store
15803  * Small helper class to make creating Stores from Array data easier.
15804  * @cfg {Number} id The array index of the record id. Leave blank to auto generate ids.
15805  * @cfg {Array} fields An array of field definition objects, or field name strings.
15806  * @cfg {Object} an existing reader (eg. copied from another store)
15807  * @cfg {Array} data The multi-dimensional array of data
15808  * @cfg {Roo.data.DataProxy} proxy [not-required]  
15809  * @cfg {Roo.data.Reader} reader  [not-required] 
15810  * @constructor
15811  * @param {Object} config
15812  */
15813 Roo.data.SimpleStore = function(config)
15814 {
15815     Roo.data.SimpleStore.superclass.constructor.call(this, {
15816         isLocal : true,
15817         reader: typeof(config.reader) != 'undefined' ? config.reader : new Roo.data.ArrayReader({
15818                 id: config.id
15819             },
15820             Roo.data.Record.create(config.fields)
15821         ),
15822         proxy : new Roo.data.MemoryProxy(config.data)
15823     });
15824     this.load();
15825 };
15826 Roo.extend(Roo.data.SimpleStore, Roo.data.Store);/*
15827  * Based on:
15828  * Ext JS Library 1.1.1
15829  * Copyright(c) 2006-2007, Ext JS, LLC.
15830  *
15831  * Originally Released Under LGPL - original licence link has changed is not relivant.
15832  *
15833  * Fork - LGPL
15834  * <script type="text/javascript">
15835  */
15836
15837 /**
15838 /**
15839  * @extends Roo.data.Store
15840  * @class Roo.data.JsonStore
15841  * Small helper class to make creating Stores for JSON data easier. <br/>
15842 <pre><code>
15843 var store = new Roo.data.JsonStore({
15844     url: 'get-images.php',
15845     root: 'images',
15846     fields: ['name', 'url', {name:'size', type: 'float'}, {name:'lastmod', type:'date'}]
15847 });
15848 </code></pre>
15849  * <b>Note: Although they are not listed, this class inherits all of the config options of Store,
15850  * JsonReader and HttpProxy (unless inline data is provided).</b>
15851  * @cfg {Array} fields An array of field definition objects, or field name strings.
15852  * @constructor
15853  * @param {Object} config
15854  */
15855 Roo.data.JsonStore = function(c){
15856     Roo.data.JsonStore.superclass.constructor.call(this, Roo.apply(c, {
15857         proxy: !c.data ? new Roo.data.HttpProxy({url: c.url}) : undefined,
15858         reader: new Roo.data.JsonReader(c, c.fields)
15859     }));
15860 };
15861 Roo.extend(Roo.data.JsonStore, Roo.data.Store);/*
15862  * Based on:
15863  * Ext JS Library 1.1.1
15864  * Copyright(c) 2006-2007, Ext JS, LLC.
15865  *
15866  * Originally Released Under LGPL - original licence link has changed is not relivant.
15867  *
15868  * Fork - LGPL
15869  * <script type="text/javascript">
15870  */
15871
15872  
15873 Roo.data.Field = function(config){
15874     if(typeof config == "string"){
15875         config = {name: config};
15876     }
15877     Roo.apply(this, config);
15878     
15879     if(!this.type){
15880         this.type = "auto";
15881     }
15882     
15883     var st = Roo.data.SortTypes;
15884     // named sortTypes are supported, here we look them up
15885     if(typeof this.sortType == "string"){
15886         this.sortType = st[this.sortType];
15887     }
15888     
15889     // set default sortType for strings and dates
15890     if(!this.sortType){
15891         switch(this.type){
15892             case "string":
15893                 this.sortType = st.asUCString;
15894                 break;
15895             case "date":
15896                 this.sortType = st.asDate;
15897                 break;
15898             default:
15899                 this.sortType = st.none;
15900         }
15901     }
15902
15903     // define once
15904     var stripRe = /[\$,%]/g;
15905
15906     // prebuilt conversion function for this field, instead of
15907     // switching every time we're reading a value
15908     if(!this.convert){
15909         var cv, dateFormat = this.dateFormat;
15910         switch(this.type){
15911             case "":
15912             case "auto":
15913             case undefined:
15914                 cv = function(v){ return v; };
15915                 break;
15916             case "string":
15917                 cv = function(v){ return (v === undefined || v === null) ? '' : String(v); };
15918                 break;
15919             case "int":
15920                 cv = function(v){
15921                     return v !== undefined && v !== null && v !== '' ?
15922                            parseInt(String(v).replace(stripRe, ""), 10) : '';
15923                     };
15924                 break;
15925             case "float":
15926                 cv = function(v){
15927                     return v !== undefined && v !== null && v !== '' ?
15928                            parseFloat(String(v).replace(stripRe, ""), 10) : ''; 
15929                     };
15930                 break;
15931             case "bool":
15932             case "boolean":
15933                 cv = function(v){ return v === true || v === "true" || v == 1; };
15934                 break;
15935             case "date":
15936                 cv = function(v){
15937                     if(!v){
15938                         return '';
15939                     }
15940                     if(v instanceof Date){
15941                         return v;
15942                     }
15943                     if(dateFormat){
15944                         if(dateFormat == "timestamp"){
15945                             return new Date(v*1000);
15946                         }
15947                         return Date.parseDate(v, dateFormat);
15948                     }
15949                     var parsed = Date.parse(v);
15950                     return parsed ? new Date(parsed) : null;
15951                 };
15952              break;
15953             
15954         }
15955         this.convert = cv;
15956     }
15957 };
15958
15959 Roo.data.Field.prototype = {
15960     dateFormat: null,
15961     defaultValue: "",
15962     mapping: null,
15963     sortType : null,
15964     sortDir : "ASC"
15965 };/*
15966  * Based on:
15967  * Ext JS Library 1.1.1
15968  * Copyright(c) 2006-2007, Ext JS, LLC.
15969  *
15970  * Originally Released Under LGPL - original licence link has changed is not relivant.
15971  *
15972  * Fork - LGPL
15973  * <script type="text/javascript">
15974  */
15975  
15976 // Base class for reading structured data from a data source.  This class is intended to be
15977 // extended (see ArrayReader, JsonReader and XmlReader) and should not be created directly.
15978
15979 /**
15980  * @class Roo.data.DataReader
15981  * @abstract
15982  * Base class for reading structured data from a data source.  This class is intended to be
15983  * extended (see {Roo.data.ArrayReader}, {Roo.data.JsonReader} and {Roo.data.XmlReader}) and should not be created directly.
15984  */
15985
15986 Roo.data.DataReader = function(meta, recordType){
15987     
15988     this.meta = meta;
15989     
15990     this.recordType = recordType instanceof Array ? 
15991         Roo.data.Record.create(recordType) : recordType;
15992 };
15993
15994 Roo.data.DataReader.prototype = {
15995     
15996     
15997     readerType : 'Data',
15998      /**
15999      * Create an empty record
16000      * @param {Object} data (optional) - overlay some values
16001      * @return {Roo.data.Record} record created.
16002      */
16003     newRow :  function(d) {
16004         var da =  {};
16005         this.recordType.prototype.fields.each(function(c) {
16006             switch( c.type) {
16007                 case 'int' : da[c.name] = 0; break;
16008                 case 'date' : da[c.name] = new Date(); break;
16009                 case 'float' : da[c.name] = 0.0; break;
16010                 case 'boolean' : da[c.name] = false; break;
16011                 default : da[c.name] = ""; break;
16012             }
16013             
16014         });
16015         return new this.recordType(Roo.apply(da, d));
16016     }
16017     
16018     
16019 };/*
16020  * Based on:
16021  * Ext JS Library 1.1.1
16022  * Copyright(c) 2006-2007, Ext JS, LLC.
16023  *
16024  * Originally Released Under LGPL - original licence link has changed is not relivant.
16025  *
16026  * Fork - LGPL
16027  * <script type="text/javascript">
16028  */
16029
16030 /**
16031  * @class Roo.data.DataProxy
16032  * @extends Roo.util.Observable
16033  * @abstract
16034  * This class is an abstract base class for implementations which provide retrieval of
16035  * unformatted data objects.<br>
16036  * <p>
16037  * DataProxy implementations are usually used in conjunction with an implementation of Roo.data.DataReader
16038  * (of the appropriate type which knows how to parse the data object) to provide a block of
16039  * {@link Roo.data.Records} to an {@link Roo.data.Store}.<br>
16040  * <p>
16041  * Custom implementations must implement the load method as described in
16042  * {@link Roo.data.HttpProxy#load}.
16043  */
16044 Roo.data.DataProxy = function(){
16045     this.addEvents({
16046         /**
16047          * @event beforeload
16048          * Fires before a network request is made to retrieve a data object.
16049          * @param {Object} This DataProxy object.
16050          * @param {Object} params The params parameter to the load function.
16051          */
16052         beforeload : true,
16053         /**
16054          * @event load
16055          * Fires before the load method's callback is called.
16056          * @param {Object} This DataProxy object.
16057          * @param {Object} o The data object.
16058          * @param {Object} arg The callback argument object passed to the load function.
16059          */
16060         load : true,
16061         /**
16062          * @event loadexception
16063          * Fires if an Exception occurs during data retrieval.
16064          * @param {Object} This DataProxy object.
16065          * @param {Object} o The data object.
16066          * @param {Object} arg The callback argument object passed to the load function.
16067          * @param {Object} e The Exception.
16068          */
16069         loadexception : true
16070     });
16071     Roo.data.DataProxy.superclass.constructor.call(this);
16072 };
16073
16074 Roo.extend(Roo.data.DataProxy, Roo.util.Observable);
16075
16076     /**
16077      * @cfg {void} listeners (Not available) Constructor blocks listeners from being set
16078      */
16079 /*
16080  * Based on:
16081  * Ext JS Library 1.1.1
16082  * Copyright(c) 2006-2007, Ext JS, LLC.
16083  *
16084  * Originally Released Under LGPL - original licence link has changed is not relivant.
16085  *
16086  * Fork - LGPL
16087  * <script type="text/javascript">
16088  */
16089 /**
16090  * @class Roo.data.MemoryProxy
16091  * @extends Roo.data.DataProxy
16092  * An implementation of Roo.data.DataProxy that simply passes the data specified in its constructor
16093  * to the Reader when its load method is called.
16094  * @constructor
16095  * @param {Object} config  A config object containing the objects needed for the Store to access data,
16096  */
16097 Roo.data.MemoryProxy = function(config){
16098     var data = config;
16099     if (typeof(config) != 'undefined' && typeof(config.data) != 'undefined') {
16100         data = config.data;
16101     }
16102     Roo.data.MemoryProxy.superclass.constructor.call(this);
16103     this.data = data;
16104 };
16105
16106 Roo.extend(Roo.data.MemoryProxy, Roo.data.DataProxy, {
16107     
16108     /**
16109      *  @cfg {Object} data The data object which the Reader uses to construct a block of Roo.data.Records.
16110      */
16111     /**
16112      * Load data from the requested source (in this case an in-memory
16113      * data object passed to the constructor), read the data object into
16114      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
16115      * process that block using the passed callback.
16116      * @param {Object} params This parameter is not used by the MemoryProxy class.
16117      * @param {Roo.data.DataReader} reader The Reader object which converts the data
16118      * object into a block of Roo.data.Records.
16119      * @param {Function} callback The function into which to pass the block of Roo.data.records.
16120      * The function must be passed <ul>
16121      * <li>The Record block object</li>
16122      * <li>The "arg" argument from the load function</li>
16123      * <li>A boolean success indicator</li>
16124      * </ul>
16125      * @param {Object} scope The scope in which to call the callback
16126      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
16127      */
16128     load : function(params, reader, callback, scope, arg){
16129         params = params || {};
16130         var result;
16131         try {
16132             result = reader.readRecords(params.data ? params.data :this.data);
16133         }catch(e){
16134             this.fireEvent("loadexception", this, arg, null, e);
16135             callback.call(scope, null, arg, false);
16136             return;
16137         }
16138         callback.call(scope, result, arg, true);
16139     },
16140     
16141     // private
16142     update : function(params, records){
16143         
16144     }
16145 });/*
16146  * Based on:
16147  * Ext JS Library 1.1.1
16148  * Copyright(c) 2006-2007, Ext JS, LLC.
16149  *
16150  * Originally Released Under LGPL - original licence link has changed is not relivant.
16151  *
16152  * Fork - LGPL
16153  * <script type="text/javascript">
16154  */
16155 /**
16156  * @class Roo.data.HttpProxy
16157  * @extends Roo.data.DataProxy
16158  * An implementation of {@link Roo.data.DataProxy} that reads a data object from an {@link Roo.data.Connection} object
16159  * configured to reference a certain URL.<br><br>
16160  * <p>
16161  * <em>Note that this class cannot be used to retrieve data from a domain other than the domain
16162  * from which the running page was served.<br><br>
16163  * <p>
16164  * For cross-domain access to remote data, use an {@link Roo.data.ScriptTagProxy}.</em><br><br>
16165  * <p>
16166  * Be aware that to enable the browser to parse an XML document, the server must set
16167  * the Content-Type header in the HTTP response to "text/xml".
16168  * @constructor
16169  * @param {Object} conn Connection config options to add to each request (e.g. {url: 'foo.php'} or
16170  * an {@link Roo.data.Connection} object.  If a Connection config is passed, the singleton {@link Roo.Ajax} object
16171  * will be used to make the request.
16172  */
16173 Roo.data.HttpProxy = function(conn){
16174     Roo.data.HttpProxy.superclass.constructor.call(this);
16175     // is conn a conn config or a real conn?
16176     this.conn = conn;
16177     this.useAjax = !conn || !conn.events;
16178   
16179 };
16180
16181 Roo.extend(Roo.data.HttpProxy, Roo.data.DataProxy, {
16182     // thse are take from connection...
16183     
16184     /**
16185      * @cfg {String} url  The default URL to be used for requests to the server. (defaults to undefined)
16186      */
16187     /**
16188      * @cfg {Object} extraParams  An object containing properties which are used as
16189      * extra parameters to each request made by this object. (defaults to undefined)
16190      */
16191     /**
16192      * @cfg {Object} defaultHeaders   An object containing request headers which are added
16193      *  to each request made by this object. (defaults to undefined)
16194      */
16195     /**
16196      * @cfg {String} method (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)
16197      */
16198     /**
16199      * @cfg {Number} timeout The timeout in milliseconds to be used for requests. (defaults to 30000)
16200      */
16201      /**
16202      * @cfg {Boolean} autoAbort Whether this request should abort any pending requests. (defaults to false)
16203      * @type Boolean
16204      */
16205   
16206
16207     /**
16208      * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
16209      * @type Boolean
16210      */
16211     /**
16212      * Return the {@link Roo.data.Connection} object being used by this Proxy.
16213      * @return {Connection} The Connection object. This object may be used to subscribe to events on
16214      * a finer-grained basis than the DataProxy events.
16215      */
16216     getConnection : function(){
16217         return this.useAjax ? Roo.Ajax : this.conn;
16218     },
16219
16220     /**
16221      * Load data from the configured {@link Roo.data.Connection}, read the data object into
16222      * a block of Roo.data.Records using the passed {@link Roo.data.DataReader} implementation, and
16223      * process that block using the passed callback.
16224      * @param {Object} params An object containing properties which are to be used as HTTP parameters
16225      * for the request to the remote server.
16226      * @param {Roo.data.DataReader} reader The Reader object which converts the data
16227      * object into a block of Roo.data.Records.
16228      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
16229      * The function must be passed <ul>
16230      * <li>The Record block object</li>
16231      * <li>The "arg" argument from the load function</li>
16232      * <li>A boolean success indicator</li>
16233      * </ul>
16234      * @param {Object} scope The scope in which to call the callback
16235      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
16236      */
16237     load : function(params, reader, callback, scope, arg){
16238         if(this.fireEvent("beforeload", this, params) !== false){
16239             var  o = {
16240                 params : params || {},
16241                 request: {
16242                     callback : callback,
16243                     scope : scope,
16244                     arg : arg
16245                 },
16246                 reader: reader,
16247                 callback : this.loadResponse,
16248                 scope: this
16249             };
16250             if(this.useAjax){
16251                 Roo.applyIf(o, this.conn);
16252                 if(this.activeRequest){
16253                     Roo.Ajax.abort(this.activeRequest);
16254                 }
16255                 this.activeRequest = Roo.Ajax.request(o);
16256             }else{
16257                 this.conn.request(o);
16258             }
16259         }else{
16260             callback.call(scope||this, null, arg, false);
16261         }
16262     },
16263
16264     // private
16265     loadResponse : function(o, success, response){
16266         delete this.activeRequest;
16267         if(!success){
16268             this.fireEvent("loadexception", this, o, response);
16269             o.request.callback.call(o.request.scope, null, o.request.arg, false);
16270             return;
16271         }
16272         var result;
16273         try {
16274             result = o.reader.read(response);
16275         }catch(e){
16276             o.success = false;
16277             o.raw = { errorMsg : response.responseText };
16278             this.fireEvent("loadexception", this, o, response, e);
16279             o.request.callback.call(o.request.scope, o, o.request.arg, false);
16280             return;
16281         }
16282         
16283         this.fireEvent("load", this, o, o.request.arg);
16284         o.request.callback.call(o.request.scope, result, o.request.arg, true);
16285     },
16286
16287     // private
16288     update : function(dataSet){
16289
16290     },
16291
16292     // private
16293     updateResponse : function(dataSet){
16294
16295     }
16296 });/*
16297  * Based on:
16298  * Ext JS Library 1.1.1
16299  * Copyright(c) 2006-2007, Ext JS, LLC.
16300  *
16301  * Originally Released Under LGPL - original licence link has changed is not relivant.
16302  *
16303  * Fork - LGPL
16304  * <script type="text/javascript">
16305  */
16306
16307 /**
16308  * @class Roo.data.ScriptTagProxy
16309  * An implementation of Roo.data.DataProxy that reads a data object from a URL which may be in a domain
16310  * other than the originating domain of the running page.<br><br>
16311  * <p>
16312  * <em>Note that if you are retrieving data from a page that is in a domain that is NOT the same as the originating domain
16313  * of the running page, you must use this class, rather than DataProxy.</em><br><br>
16314  * <p>
16315  * The content passed back from a server resource requested by a ScriptTagProxy is executable JavaScript
16316  * source code that is used as the source inside a &lt;script> tag.<br><br>
16317  * <p>
16318  * In order for the browser to process the returned data, the server must wrap the data object
16319  * with a call to a callback function, the name of which is passed as a parameter by the ScriptTagProxy.
16320  * Below is a Java example for a servlet which returns data for either a ScriptTagProxy, or an HttpProxy
16321  * depending on whether the callback name was passed:
16322  * <p>
16323  * <pre><code>
16324 boolean scriptTag = false;
16325 String cb = request.getParameter("callback");
16326 if (cb != null) {
16327     scriptTag = true;
16328     response.setContentType("text/javascript");
16329 } else {
16330     response.setContentType("application/x-json");
16331 }
16332 Writer out = response.getWriter();
16333 if (scriptTag) {
16334     out.write(cb + "(");
16335 }
16336 out.print(dataBlock.toJsonString());
16337 if (scriptTag) {
16338     out.write(");");
16339 }
16340 </pre></code>
16341  *
16342  * @constructor
16343  * @param {Object} config A configuration object.
16344  */
16345 Roo.data.ScriptTagProxy = function(config){
16346     Roo.data.ScriptTagProxy.superclass.constructor.call(this);
16347     Roo.apply(this, config);
16348     this.head = document.getElementsByTagName("head")[0];
16349 };
16350
16351 Roo.data.ScriptTagProxy.TRANS_ID = 1000;
16352
16353 Roo.extend(Roo.data.ScriptTagProxy, Roo.data.DataProxy, {
16354     /**
16355      * @cfg {String} url The URL from which to request the data object.
16356      */
16357     /**
16358      * @cfg {Number} timeout (Optional) The number of milliseconds to wait for a response. Defaults to 30 seconds.
16359      */
16360     timeout : 30000,
16361     /**
16362      * @cfg {String} callbackParam (Optional) The name of the parameter to pass to the server which tells
16363      * the server the name of the callback function set up by the load call to process the returned data object.
16364      * Defaults to "callback".<p>The server-side processing must read this parameter value, and generate
16365      * javascript output which calls this named function passing the data object as its only parameter.
16366      */
16367     callbackParam : "callback",
16368     /**
16369      *  @cfg {Boolean} nocache (Optional) Defaults to true. Disable cacheing by adding a unique parameter
16370      * name to the request.
16371      */
16372     nocache : true,
16373
16374     /**
16375      * Load data from the configured URL, read the data object into
16376      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
16377      * process that block using the passed callback.
16378      * @param {Object} params An object containing properties which are to be used as HTTP parameters
16379      * for the request to the remote server.
16380      * @param {Roo.data.DataReader} reader The Reader object which converts the data
16381      * object into a block of Roo.data.Records.
16382      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
16383      * The function must be passed <ul>
16384      * <li>The Record block object</li>
16385      * <li>The "arg" argument from the load function</li>
16386      * <li>A boolean success indicator</li>
16387      * </ul>
16388      * @param {Object} scope The scope in which to call the callback
16389      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
16390      */
16391     load : function(params, reader, callback, scope, arg){
16392         if(this.fireEvent("beforeload", this, params) !== false){
16393
16394             var p = Roo.urlEncode(Roo.apply(params, this.extraParams));
16395
16396             var url = this.url;
16397             url += (url.indexOf("?") != -1 ? "&" : "?") + p;
16398             if(this.nocache){
16399                 url += "&_dc=" + (new Date().getTime());
16400             }
16401             var transId = ++Roo.data.ScriptTagProxy.TRANS_ID;
16402             var trans = {
16403                 id : transId,
16404                 cb : "stcCallback"+transId,
16405                 scriptId : "stcScript"+transId,
16406                 params : params,
16407                 arg : arg,
16408                 url : url,
16409                 callback : callback,
16410                 scope : scope,
16411                 reader : reader
16412             };
16413             var conn = this;
16414
16415             window[trans.cb] = function(o){
16416                 conn.handleResponse(o, trans);
16417             };
16418
16419             url += String.format("&{0}={1}", this.callbackParam, trans.cb);
16420
16421             if(this.autoAbort !== false){
16422                 this.abort();
16423             }
16424
16425             trans.timeoutId = this.handleFailure.defer(this.timeout, this, [trans]);
16426
16427             var script = document.createElement("script");
16428             script.setAttribute("src", url);
16429             script.setAttribute("type", "text/javascript");
16430             script.setAttribute("id", trans.scriptId);
16431             this.head.appendChild(script);
16432
16433             this.trans = trans;
16434         }else{
16435             callback.call(scope||this, null, arg, false);
16436         }
16437     },
16438
16439     // private
16440     isLoading : function(){
16441         return this.trans ? true : false;
16442     },
16443
16444     /**
16445      * Abort the current server request.
16446      */
16447     abort : function(){
16448         if(this.isLoading()){
16449             this.destroyTrans(this.trans);
16450         }
16451     },
16452
16453     // private
16454     destroyTrans : function(trans, isLoaded){
16455         this.head.removeChild(document.getElementById(trans.scriptId));
16456         clearTimeout(trans.timeoutId);
16457         if(isLoaded){
16458             window[trans.cb] = undefined;
16459             try{
16460                 delete window[trans.cb];
16461             }catch(e){}
16462         }else{
16463             // if hasn't been loaded, wait for load to remove it to prevent script error
16464             window[trans.cb] = function(){
16465                 window[trans.cb] = undefined;
16466                 try{
16467                     delete window[trans.cb];
16468                 }catch(e){}
16469             };
16470         }
16471     },
16472
16473     // private
16474     handleResponse : function(o, trans){
16475         this.trans = false;
16476         this.destroyTrans(trans, true);
16477         var result;
16478         try {
16479             result = trans.reader.readRecords(o);
16480         }catch(e){
16481             this.fireEvent("loadexception", this, o, trans.arg, e);
16482             trans.callback.call(trans.scope||window, null, trans.arg, false);
16483             return;
16484         }
16485         this.fireEvent("load", this, o, trans.arg);
16486         trans.callback.call(trans.scope||window, result, trans.arg, true);
16487     },
16488
16489     // private
16490     handleFailure : function(trans){
16491         this.trans = false;
16492         this.destroyTrans(trans, false);
16493         this.fireEvent("loadexception", this, null, trans.arg);
16494         trans.callback.call(trans.scope||window, null, trans.arg, false);
16495     }
16496 });/*
16497  * Based on:
16498  * Ext JS Library 1.1.1
16499  * Copyright(c) 2006-2007, Ext JS, LLC.
16500  *
16501  * Originally Released Under LGPL - original licence link has changed is not relivant.
16502  *
16503  * Fork - LGPL
16504  * <script type="text/javascript">
16505  */
16506
16507 /**
16508  * @class Roo.data.JsonReader
16509  * @extends Roo.data.DataReader
16510  * Data reader class to create an Array of Roo.data.Record objects from a JSON response
16511  * based on mappings in a provided Roo.data.Record constructor.
16512  * 
16513  * The default behaviour of a store is to send ?_requestMeta=1, unless the class has recieved 'metaData' property
16514  * in the reply previously. 
16515  * 
16516  * <p>
16517  * Example code:
16518  * <pre><code>
16519 var RecordDef = Roo.data.Record.create([
16520     {name: 'name', mapping: 'name'},     // "mapping" property not needed if it's the same as "name"
16521     {name: 'occupation'}                 // This field will use "occupation" as the mapping.
16522 ]);
16523 var myReader = new Roo.data.JsonReader({
16524     totalProperty: "results",    // The property which contains the total dataset size (optional)
16525     root: "rows",                // The property which contains an Array of row objects
16526     id: "id"                     // The property within each row object that provides an ID for the record (optional)
16527 }, RecordDef);
16528 </code></pre>
16529  * <p>
16530  * This would consume a JSON file like this:
16531  * <pre><code>
16532 { 'results': 2, 'rows': [
16533     { 'id': 1, 'name': 'Bill', occupation: 'Gardener' },
16534     { 'id': 2, 'name': 'Ben', occupation: 'Horticulturalist' } ]
16535 }
16536 </code></pre>
16537  * @cfg {String} totalProperty Name of the property from which to retrieve the total number of records
16538  * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
16539  * paged from the remote server.
16540  * @cfg {String} successProperty Name of the property from which to retrieve the success attribute used by forms.
16541  * @cfg {String} root name of the property which contains the Array of row objects.
16542  * @cfg {String} id Name of the property within a row object that contains a record identifier value.
16543  * @cfg {Array} fields Array of field definition objects
16544  * @constructor
16545  * Create a new JsonReader
16546  * @param {Object} meta Metadata configuration options
16547  * @param {Object} recordType Either an Array of field definition objects,
16548  * or an {@link Roo.data.Record} object created using {@link Roo.data.Record#create}.
16549  */
16550 Roo.data.JsonReader = function(meta, recordType){
16551     
16552     meta = meta || {};
16553     // set some defaults:
16554     Roo.applyIf(meta, {
16555         totalProperty: 'total',
16556         successProperty : 'success',
16557         root : 'data',
16558         id : 'id'
16559     });
16560     
16561     Roo.data.JsonReader.superclass.constructor.call(this, meta, recordType||meta.fields);
16562 };
16563 Roo.extend(Roo.data.JsonReader, Roo.data.DataReader, {
16564     
16565     readerType : 'Json',
16566     
16567     /**
16568      * @prop {Boolean} metaFromRemote  - if the meta data was loaded from the remote source.
16569      * Used by Store query builder to append _requestMeta to params.
16570      * 
16571      */
16572     metaFromRemote : false,
16573     /**
16574      * This method is only used by a DataProxy which has retrieved data from a remote server.
16575      * @param {Object} response The XHR object which contains the JSON data in its responseText.
16576      * @return {Object} data A data block which is used by an Roo.data.Store object as
16577      * a cache of Roo.data.Records.
16578      */
16579     read : function(response){
16580         var json = response.responseText;
16581        
16582         var o = /* eval:var:o */ eval("("+json+")");
16583         if(!o) {
16584             throw {message: "JsonReader.read: Json object not found"};
16585         }
16586         
16587         if(o.metaData){
16588             
16589             delete this.ef;
16590             this.metaFromRemote = true;
16591             this.meta = o.metaData;
16592             this.recordType = Roo.data.Record.create(o.metaData.fields);
16593             this.onMetaChange(this.meta, this.recordType, o);
16594         }
16595         return this.readRecords(o);
16596     },
16597
16598     // private function a store will implement
16599     onMetaChange : function(meta, recordType, o){
16600
16601     },
16602
16603     /**
16604          * @ignore
16605          */
16606     simpleAccess: function(obj, subsc) {
16607         return obj[subsc];
16608     },
16609
16610         /**
16611          * @ignore
16612          */
16613     getJsonAccessor: function(){
16614         var re = /[\[\.]/;
16615         return function(expr) {
16616             try {
16617                 return(re.test(expr))
16618                     ? new Function("obj", "return obj." + expr)
16619                     : function(obj){
16620                         return obj[expr];
16621                     };
16622             } catch(e){}
16623             return Roo.emptyFn;
16624         };
16625     }(),
16626
16627     /**
16628      * Create a data block containing Roo.data.Records from an XML document.
16629      * @param {Object} o An object which contains an Array of row objects in the property specified
16630      * in the config as 'root, and optionally a property, specified in the config as 'totalProperty'
16631      * which contains the total size of the dataset.
16632      * @return {Object} data A data block which is used by an Roo.data.Store object as
16633      * a cache of Roo.data.Records.
16634      */
16635     readRecords : function(o){
16636         /**
16637          * After any data loads, the raw JSON data is available for further custom processing.
16638          * @type Object
16639          */
16640         this.o = o;
16641         var s = this.meta, Record = this.recordType,
16642             f = Record ? Record.prototype.fields : null, fi = f ? f.items : [], fl = f ? f.length : 0;
16643
16644 //      Generate extraction functions for the totalProperty, the root, the id, and for each field
16645         if (!this.ef) {
16646             if(s.totalProperty) {
16647                     this.getTotal = this.getJsonAccessor(s.totalProperty);
16648                 }
16649                 if(s.successProperty) {
16650                     this.getSuccess = this.getJsonAccessor(s.successProperty);
16651                 }
16652                 this.getRoot = s.root ? this.getJsonAccessor(s.root) : function(p){return p;};
16653                 if (s.id) {
16654                         var g = this.getJsonAccessor(s.id);
16655                         this.getId = function(rec) {
16656                                 var r = g(rec);  
16657                                 return (r === undefined || r === "") ? null : r;
16658                         };
16659                 } else {
16660                         this.getId = function(){return null;};
16661                 }
16662             this.ef = [];
16663             for(var jj = 0; jj < fl; jj++){
16664                 f = fi[jj];
16665                 var map = (f.mapping !== undefined && f.mapping !== null) ? f.mapping : f.name;
16666                 this.ef[jj] = this.getJsonAccessor(map);
16667             }
16668         }
16669
16670         var root = this.getRoot(o), c = root.length, totalRecords = c, success = true;
16671         if(s.totalProperty){
16672             var vt = parseInt(this.getTotal(o), 10);
16673             if(!isNaN(vt)){
16674                 totalRecords = vt;
16675             }
16676         }
16677         if(s.successProperty){
16678             var vs = this.getSuccess(o);
16679             if(vs === false || vs === 'false'){
16680                 success = false;
16681             }
16682         }
16683         var records = [];
16684         for(var i = 0; i < c; i++){
16685             var n = root[i];
16686             var values = {};
16687             var id = this.getId(n);
16688             for(var j = 0; j < fl; j++){
16689                 f = fi[j];
16690                                 var v = this.ef[j](n);
16691                                 if (!f.convert) {
16692                                         Roo.log('missing convert for ' + f.name);
16693                                         Roo.log(f);
16694                                         continue;
16695                                 }
16696                                 values[f.name] = f.convert((v !== undefined) ? v : f.defaultValue);
16697             }
16698                         if (!Record) {
16699                                 return {
16700                                         raw : { errorMsg : "JSON Reader Error: fields or metadata not available to create Record" },
16701                                         success : false,
16702                                         records : [],
16703                                         totalRecords : 0
16704                                 };
16705                         }
16706             var record = new Record(values, id);
16707             record.json = n;
16708             records[i] = record;
16709         }
16710         return {
16711             raw : o,
16712             success : success,
16713             records : records,
16714             totalRecords : totalRecords
16715         };
16716     },
16717     // used when loading children.. @see loadDataFromChildren
16718     toLoadData: function(rec)
16719     {
16720         // expect rec just to be an array.. eg [a,b,c, [...] << cn ]
16721         var data = typeof(rec.data.cn) == 'undefined' ? [] : rec.data.cn;
16722         return { data : data, total : data.length };
16723         
16724     }
16725 });/*
16726  * Based on:
16727  * Ext JS Library 1.1.1
16728  * Copyright(c) 2006-2007, Ext JS, LLC.
16729  *
16730  * Originally Released Under LGPL - original licence link has changed is not relivant.
16731  *
16732  * Fork - LGPL
16733  * <script type="text/javascript">
16734  */
16735
16736 /**
16737  * @class Roo.data.ArrayReader
16738  * @extends Roo.data.DataReader
16739  * Data reader class to create an Array of Roo.data.Record objects from an Array.
16740  * Each element of that Array represents a row of data fields. The
16741  * fields are pulled into a Record object using as a subscript, the <em>mapping</em> property
16742  * of the field definition if it exists, or the field's ordinal position in the definition.<br>
16743  * <p>
16744  * Example code:.
16745  * <pre><code>
16746 var RecordDef = Roo.data.Record.create([
16747     {name: 'name', mapping: 1},         // "mapping" only needed if an "id" field is present which
16748     {name: 'occupation', mapping: 2}    // precludes using the ordinal position as the index.
16749 ]);
16750 var myReader = new Roo.data.ArrayReader({
16751     id: 0                     // The subscript within row Array that provides an ID for the Record (optional)
16752 }, RecordDef);
16753 </code></pre>
16754  * <p>
16755  * This would consume an Array like this:
16756  * <pre><code>
16757 [ [1, 'Bill', 'Gardener'], [2, 'Ben', 'Horticulturalist'] ]
16758   </code></pre>
16759  
16760  * @constructor
16761  * Create a new JsonReader
16762  * @param {Object} meta Metadata configuration options.
16763  * @param {Object|Array} recordType Either an Array of field definition objects
16764  * 
16765  * @cfg {Array} fields Array of field definition objects
16766  * @cfg {String} id Name of the property within a row object that contains a record identifier value.
16767  * as specified to {@link Roo.data.Record#create},
16768  * or an {@link Roo.data.Record} object
16769  *
16770  * 
16771  * created using {@link Roo.data.Record#create}.
16772  */
16773 Roo.data.ArrayReader = function(meta, recordType)
16774 {    
16775     Roo.data.ArrayReader.superclass.constructor.call(this, meta, recordType||meta.fields);
16776 };
16777
16778 Roo.extend(Roo.data.ArrayReader, Roo.data.JsonReader, {
16779     
16780       /**
16781      * Create a data block containing Roo.data.Records from an XML document.
16782      * @param {Object} o An Array of row objects which represents the dataset.
16783      * @return {Object} A data block which is used by an {@link Roo.data.Store} object as
16784      * a cache of Roo.data.Records.
16785      */
16786     readRecords : function(o)
16787     {
16788         var sid = this.meta ? this.meta.id : null;
16789         var recordType = this.recordType, fields = recordType.prototype.fields;
16790         var records = [];
16791         var root = o;
16792         for(var i = 0; i < root.length; i++){
16793             var n = root[i];
16794             var values = {};
16795             var id = ((sid || sid === 0) && n[sid] !== undefined && n[sid] !== "" ? n[sid] : null);
16796             for(var j = 0, jlen = fields.length; j < jlen; j++){
16797                 var f = fields.items[j];
16798                 var k = f.mapping !== undefined && f.mapping !== null ? f.mapping : j;
16799                 var v = n[k] !== undefined ? n[k] : f.defaultValue;
16800                 v = f.convert(v);
16801                 values[f.name] = v;
16802             }
16803             var record = new recordType(values, id);
16804             record.json = n;
16805             records[records.length] = record;
16806         }
16807         return {
16808             records : records,
16809             totalRecords : records.length
16810         };
16811     },
16812     // used when loading children.. @see loadDataFromChildren
16813     toLoadData: function(rec)
16814     {
16815         // expect rec just to be an array.. eg [a,b,c, [...] << cn ]
16816         return typeof(rec.data.cn) == 'undefined' ? [] : rec.data.cn;
16817         
16818     }
16819     
16820     
16821 });/*
16822  * - LGPL
16823  * * 
16824  */
16825
16826 /**
16827  * @class Roo.bootstrap.form.ComboBox
16828  * @extends Roo.bootstrap.form.TriggerField
16829  * A combobox control with support for autocomplete, remote-loading, paging and many other features.
16830  * @cfg {Boolean} append (true|false) default false
16831  * @cfg {Boolean} autoFocus (true|false) auto focus the first item, default true
16832  * @cfg {Boolean} tickable ComboBox with tickable selections (true|false), default false
16833  * @cfg {Boolean} triggerList trigger show the list or not (true|false) default true
16834  * @cfg {Boolean} showToggleBtn show toggle button or not (true|false) default true
16835  * @cfg {String} btnPosition set the position of the trigger button (left | right) default right
16836  * @cfg {Boolean} animate default true
16837  * @cfg {Boolean} emptyResultText only for touch device
16838  * @cfg {String} triggerText multiple combobox trigger button text default 'Select'
16839  * @cfg {String} emptyTitle default ''
16840  * @cfg {Number} width fixed with? experimental
16841  * @constructor
16842  * Create a new ComboBox.
16843  * @param {Object} config Configuration options
16844  */
16845 Roo.bootstrap.form.ComboBox = function(config){
16846     Roo.bootstrap.form.ComboBox.superclass.constructor.call(this, config);
16847     this.addEvents({
16848         /**
16849          * @event expand
16850          * Fires when the dropdown list is expanded
16851         * @param {Roo.bootstrap.form.ComboBox} combo This combo box
16852         */
16853         'expand' : true,
16854         /**
16855          * @event collapse
16856          * Fires when the dropdown list is collapsed
16857         * @param {Roo.bootstrap.form.ComboBox} combo This combo box
16858         */
16859         'collapse' : true,
16860         /**
16861          * @event beforeselect
16862          * Fires before a list item is selected. Return false to cancel the selection.
16863         * @param {Roo.bootstrap.form.ComboBox} combo This combo box
16864         * @param {Roo.data.Record} record The data record returned from the underlying store
16865         * @param {Number} index The index of the selected item in the dropdown list
16866         */
16867         'beforeselect' : true,
16868         /**
16869          * @event select
16870          * Fires when a list item is selected
16871         * @param {Roo.bootstrap.form.ComboBox} combo This combo box
16872         * @param {Roo.data.Record} record The data record returned from the underlying store (or false on clear)
16873         * @param {Number} index The index of the selected item in the dropdown list
16874         */
16875         'select' : true,
16876         /**
16877          * @event beforequery
16878          * Fires before all queries are processed. Return false to cancel the query or set cancel to true.
16879          * The event object passed has these properties:
16880         * @param {Roo.bootstrap.form.ComboBox} combo This combo box
16881         * @param {String} query The query
16882         * @param {Boolean} forceAll true to force "all" query
16883         * @param {Boolean} cancel true to cancel the query
16884         * @param {Object} e The query event object
16885         */
16886         'beforequery': true,
16887          /**
16888          * @event add
16889          * Fires when the 'add' icon is pressed (add a listener to enable add button)
16890         * @param {Roo.bootstrap.form.ComboBox} combo This combo box
16891         */
16892         'add' : true,
16893         /**
16894          * @event edit
16895          * Fires when the 'edit' icon is pressed (add a listener to enable add button)
16896         * @param {Roo.bootstrap.form.ComboBox} combo This combo box
16897         * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
16898         */
16899         'edit' : true,
16900         /**
16901          * @event remove
16902          * Fires when the remove value from the combobox array
16903         * @param {Roo.bootstrap.form.ComboBox} combo This combo box
16904         */
16905         'remove' : true,
16906         /**
16907          * @event afterremove
16908          * Fires when the remove value from the combobox array
16909         * @param {Roo.bootstrap.form.ComboBox} combo This combo box
16910         */
16911         'afterremove' : true,
16912         /**
16913          * @event specialfilter
16914          * Fires when specialfilter
16915             * @param {Roo.bootstrap.form.ComboBox} combo This combo box
16916             */
16917         'specialfilter' : true,
16918         /**
16919          * @event tick
16920          * Fires when tick the element
16921             * @param {Roo.bootstrap.form.ComboBox} combo This combo box
16922             */
16923         'tick' : true,
16924         /**
16925          * @event touchviewdisplay
16926          * Fires when touch view require special display (default is using displayField)
16927             * @param {Roo.bootstrap.form.ComboBox} combo This combo box
16928             * @param {Object} cfg set html .
16929             */
16930         'touchviewdisplay' : true
16931         
16932     });
16933     
16934     this.item = [];
16935     this.tickItems = [];
16936     
16937     this.selectedIndex = -1;
16938     if(this.mode == 'local'){
16939         if(config.queryDelay === undefined){
16940             this.queryDelay = 10;
16941         }
16942         if(config.minChars === undefined){
16943             this.minChars = 0;
16944         }
16945     }
16946 };
16947
16948 Roo.extend(Roo.bootstrap.form.ComboBox, Roo.bootstrap.form.TriggerField, {
16949      
16950     /**
16951      * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
16952      * rendering into an Roo.Editor, defaults to false)
16953      */
16954     /**
16955      * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
16956      * {tag: "input", type: "text", size: "24", autocomplete: "off"})
16957      */
16958     /**
16959      * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
16960      */
16961     /**
16962      * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
16963      * the dropdown list (defaults to undefined, with no header element)
16964      */
16965
16966      /**
16967      * @cfg {String/Roo.Template} tpl The template to use to render the output default is  '<a class="dropdown-item" href="#">{' + this.displayField + '}</a>' 
16968      */
16969      
16970      /**
16971      * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
16972      */
16973     listWidth: undefined,
16974     /**
16975      * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
16976      * mode = 'remote' or 'text' if mode = 'local')
16977      */
16978     displayField: undefined,
16979     
16980     /**
16981      * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
16982      * mode = 'remote' or 'value' if mode = 'local'). 
16983      * Note: use of a valueField requires the user make a selection
16984      * in order for a value to be mapped.
16985      */
16986     valueField: undefined,
16987     /**
16988      * @cfg {String} modalTitle The title of the dialog that pops up on mobile views.
16989      */
16990     modalTitle : '',
16991     
16992     /**
16993      * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
16994      * field's data value (defaults to the underlying DOM element's name)
16995      */
16996     hiddenName: undefined,
16997     /**
16998      * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
16999      */
17000     listClass: '',
17001     /**
17002      * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
17003      */
17004     selectedClass: 'active',
17005     
17006     /**
17007      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
17008      */
17009     shadow:'sides',
17010     /**
17011      * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
17012      * anchor positions (defaults to 'tl-bl')
17013      */
17014     listAlign: 'tl-bl?',
17015     /**
17016      * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
17017      */
17018     maxHeight: 300,
17019     /**
17020      * @cfg {String} triggerAction The action to execute when the trigger field is activated.  Use 'all' to run the
17021      * query specified by the allQuery config option (defaults to 'query')
17022      */
17023     triggerAction: 'query',
17024     /**
17025      * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
17026      * (defaults to 4, does not apply if editable = false)
17027      */
17028     minChars : 4,
17029     /**
17030      * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
17031      * delay (typeAheadDelay) if it matches a known value (defaults to false)
17032      */
17033     typeAhead: false,
17034     /**
17035      * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
17036      * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
17037      */
17038     queryDelay: 500,
17039     /**
17040      * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
17041      * filter queries will execute with page start and limit parameters.  Only applies when mode = 'remote' (defaults to 0)
17042      */
17043     pageSize: 0,
17044     /**
17045      * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus.  Only applies
17046      * when editable = true (defaults to false)
17047      */
17048     selectOnFocus:false,
17049     /**
17050      * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
17051      */
17052     queryParam: 'query',
17053     /**
17054      * @cfg {String} loadingText The text to display in the dropdown list while data is loading.  Only applies
17055      * when mode = 'remote' (defaults to 'Loading...')
17056      */
17057     loadingText: 'Loading...',
17058     /**
17059      * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
17060      */
17061     resizable: false,
17062     /**
17063      * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
17064      */
17065     handleHeight : 8,
17066     /**
17067      * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
17068      * traditional select (defaults to true)
17069      */
17070     editable: true,
17071     /**
17072      * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
17073      */
17074     allQuery: '',
17075     /**
17076      * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
17077      */
17078     mode: 'remote',
17079     /**
17080      * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
17081      * listWidth has a higher value)
17082      */
17083     minListWidth : 70,
17084     /**
17085      * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
17086      * allow the user to set arbitrary text into the field (defaults to false)
17087      */
17088     forceSelection:false,
17089     /**
17090      * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
17091      * if typeAhead = true (defaults to 250)
17092      */
17093     typeAheadDelay : 250,
17094     /**
17095      * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
17096      * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
17097      */
17098     valueNotFoundText : undefined,
17099     /**
17100      * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
17101      */
17102     blockFocus : false,
17103     
17104     /**
17105      * @cfg {Boolean} disableClear Disable showing of clear button.
17106      */
17107     disableClear : false,
17108     /**
17109      * @cfg {Boolean} alwaysQuery  Disable caching of results, and always send query
17110      */
17111     alwaysQuery : false,
17112     
17113     /**
17114      * @cfg {Boolean} multiple  (true|false) ComboBobArray, default false
17115      */
17116     multiple : false,
17117     
17118     /**
17119      * @cfg {String} invalidClass DEPRICATED - uses BS4 is-valid now
17120      */
17121     invalidClass : "has-warning",
17122     
17123     /**
17124      * @cfg {String} validClass DEPRICATED - uses BS4 is-valid now
17125      */
17126     validClass : "has-success",
17127     
17128     /**
17129      * @cfg {Boolean} specialFilter (true|false) special filter default false
17130      */
17131     specialFilter : false,
17132     
17133     /**
17134      * @cfg {Boolean} mobileTouchView (true|false) show mobile touch view when using a mobile default true
17135      */
17136     mobileTouchView : true,
17137     
17138     /**
17139      * @cfg {Boolean} useNativeIOS (true|false) render it as classic select for ios, not support dynamic load data (default false)
17140      */
17141     useNativeIOS : false,
17142     
17143     /**
17144      * @cfg {Boolean} mobile_restrict_height (true|false) restrict height for touch view
17145      */
17146     mobile_restrict_height : false,
17147     
17148     ios_options : false,
17149     
17150     //private
17151     addicon : false,
17152     editicon: false,
17153     
17154     page: 0,
17155     hasQuery: false,
17156     append: false,
17157     loadNext: false,
17158     autoFocus : true,
17159     tickable : false,
17160     btnPosition : 'right',
17161     triggerList : true,
17162     showToggleBtn : true,
17163     animate : true,
17164     emptyResultText: 'Empty',
17165     triggerText : 'Select',
17166     emptyTitle : '',
17167     width : false,
17168     
17169     // element that contains real text value.. (when hidden is used..)
17170     
17171     getAutoCreate : function()
17172     {   
17173         var cfg = false;
17174         //render
17175         /*
17176          * Render classic select for iso
17177          */
17178         
17179         if(Roo.isIOS && this.useNativeIOS){
17180             cfg = this.getAutoCreateNativeIOS();
17181             return cfg;
17182         }
17183         
17184         /*
17185          * Touch Devices
17186          */
17187         
17188         if(Roo.isTouch && this.mobileTouchView){
17189             cfg = this.getAutoCreateTouchView();
17190             return cfg;;
17191         }
17192         
17193         /*
17194          *  Normal ComboBox
17195          */
17196         if(!this.tickable){
17197             cfg = Roo.bootstrap.form.ComboBox.superclass.getAutoCreate.call(this);
17198             return cfg;
17199         }
17200         
17201         /*
17202          *  ComboBox with tickable selections
17203          */
17204              
17205         var align = this.labelAlign || this.parentLabelAlign();
17206         
17207         cfg = {
17208             cls : 'form-group roo-combobox-tickable' //input-group
17209         };
17210         
17211         var btn_text_select = '';
17212         var btn_text_done = '';
17213         var btn_text_cancel = '';
17214         
17215         if (this.btn_text_show) {
17216             btn_text_select = 'Select';
17217             btn_text_done = 'Done';
17218             btn_text_cancel = 'Cancel'; 
17219         }
17220         
17221         var buttons = {
17222             tag : 'div',
17223             cls : 'tickable-buttons',
17224             cn : [
17225                 {
17226                     tag : 'button',
17227                     type : 'button',
17228                     cls : 'btn btn-link btn-edit pull-' + this.btnPosition,
17229                     //html : this.triggerText
17230                     html: btn_text_select
17231                 },
17232                 {
17233                     tag : 'button',
17234                     type : 'button',
17235                     name : 'ok',
17236                     cls : 'btn btn-link btn-ok pull-' + this.btnPosition,
17237                     //html : 'Done'
17238                     html: btn_text_done
17239                 },
17240                 {
17241                     tag : 'button',
17242                     type : 'button',
17243                     name : 'cancel',
17244                     cls : 'btn btn-link btn-cancel pull-' + this.btnPosition,
17245                     //html : 'Cancel'
17246                     html: btn_text_cancel
17247                 }
17248             ]
17249         };
17250         
17251         if(this.editable){
17252             buttons.cn.unshift({
17253                 tag: 'input',
17254                 cls: 'roo-select2-search-field-input'
17255             });
17256         }
17257         
17258         var _this = this;
17259         
17260         Roo.each(buttons.cn, function(c){
17261             if (_this.size) {
17262                 c.cls += ' btn-' + _this.size;
17263             }
17264
17265             if (_this.disabled) {
17266                 c.disabled = true;
17267             }
17268         });
17269         
17270         var box = {
17271             tag: 'div',
17272             style : 'display: contents',
17273             cn: [
17274                 {
17275                     tag: 'input',
17276                     type : 'hidden',
17277                     cls: 'form-hidden-field'
17278                 },
17279                 {
17280                     tag: 'ul',
17281                     cls: 'roo-select2-choices',
17282                     cn:[
17283                         {
17284                             tag: 'li',
17285                             cls: 'roo-select2-search-field',
17286                             cn: [
17287                                 buttons
17288                             ]
17289                         }
17290                     ]
17291                 }
17292             ]
17293         };
17294         
17295         var combobox = {
17296             cls: 'roo-select2-container input-group roo-select2-container-multi',
17297             cn: [
17298                 
17299                 box
17300 //                {
17301 //                    tag: 'ul',
17302 //                    cls: 'typeahead typeahead-long dropdown-menu',
17303 //                    style: 'display:none; max-height:' + this.maxHeight + 'px;'
17304 //                }
17305             ]
17306         };
17307         
17308         if(this.hasFeedback && !this.allowBlank){
17309             
17310             var feedback = {
17311                 tag: 'span',
17312                 cls: 'glyphicon form-control-feedback'
17313             };
17314
17315             combobox.cn.push(feedback);
17316         }
17317         
17318         
17319         
17320         var indicator = {
17321             tag : 'i',
17322             cls : 'roo-required-indicator ' + (this.indicatorpos == 'right'  ? 'right' : 'left') +'-indicator text-danger fa fa-lg fa-star',
17323             tooltip : 'This field is required'
17324         };
17325          
17326         if (this.allowBlank) {
17327             indicator = {
17328                 tag : 'i',
17329                 style : 'display:none'
17330             };
17331         } 
17332         if (align ==='left' && this.fieldLabel.length) {
17333             
17334             cfg.cls += ' roo-form-group-label-left'  + (Roo.bootstrap.version == 4 ? ' row' : '');
17335             
17336             cfg.cn = [
17337                 indicator,
17338                 {
17339                     tag: 'label',
17340                     'for' :  id,
17341                     cls : 'control-label col-form-label',
17342                     html : this.fieldLabel
17343
17344                 },
17345                 {
17346                     cls : "", 
17347                     cn: [
17348                         combobox
17349                     ]
17350                 }
17351
17352             ];
17353             
17354             var labelCfg = cfg.cn[1];
17355             var contentCfg = cfg.cn[2];
17356             
17357
17358             if(this.indicatorpos == 'right'){
17359                 
17360                 cfg.cn = [
17361                     {
17362                         tag: 'label',
17363                         'for' :  id,
17364                         cls : 'control-label col-form-label',
17365                         cn : [
17366                             {
17367                                 tag : 'span',
17368                                 html : this.fieldLabel
17369                             },
17370                             indicator
17371                         ]
17372                     },
17373                     {
17374                         cls : "",
17375                         cn: [
17376                             combobox
17377                         ]
17378                     }
17379
17380                 ];
17381                 
17382                 
17383                 
17384                 labelCfg = cfg.cn[0];
17385                 contentCfg = cfg.cn[1];
17386             
17387             }
17388             
17389             if(this.labelWidth > 12){
17390                 labelCfg.style = "width: " + this.labelWidth + 'px';
17391             }
17392             if(this.width * 1 > 0){
17393                 contentCfg.style = "width: " + this.width + 'px';
17394             }
17395             if(this.labelWidth < 13 && this.labelmd == 0){
17396                 this.labelmd = this.labelWidth;
17397             }
17398             
17399             if(this.labellg > 0){
17400                 labelCfg.cls += ' col-lg-' + this.labellg;
17401                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
17402             }
17403             
17404             if(this.labelmd > 0){
17405                 labelCfg.cls += ' col-md-' + this.labelmd;
17406                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
17407             }
17408             
17409             if(this.labelsm > 0){
17410                 labelCfg.cls += ' col-sm-' + this.labelsm;
17411                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
17412             }
17413             
17414             if(this.labelxs > 0){
17415                 labelCfg.cls += ' col-xs-' + this.labelxs;
17416                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
17417             }
17418                 
17419                 
17420         } else if ( this.fieldLabel.length) {
17421 //                Roo.log(" label");
17422                  cfg.cn = [
17423                    indicator,
17424                     {
17425                         tag: 'label',
17426                         //cls : 'input-group-addon',
17427                         html : this.fieldLabel
17428                     },
17429                     combobox
17430                 ];
17431                 
17432                 if(this.indicatorpos == 'right'){
17433                     cfg.cn = [
17434                         {
17435                             tag: 'label',
17436                             //cls : 'input-group-addon',
17437                             html : this.fieldLabel
17438                         },
17439                         indicator,
17440                         combobox
17441                     ];
17442                     
17443                 }
17444
17445         } else {
17446             
17447 //                Roo.log(" no label && no align");
17448                 cfg = combobox
17449                      
17450                 
17451         }
17452          
17453         var settings=this;
17454         ['xs','sm','md','lg'].map(function(size){
17455             if (settings[size]) {
17456                 cfg.cls += ' col-' + size + '-' + settings[size];
17457             }
17458         });
17459         
17460         return cfg;
17461         
17462     },
17463     
17464     _initEventsCalled : false,
17465     
17466     // private
17467     initEvents: function()
17468     {   
17469         if (this._initEventsCalled) { // as we call render... prevent looping...
17470             return;
17471         }
17472         this._initEventsCalled = true;
17473         
17474         if (!this.store) {
17475             throw "can not find store for combo";
17476         }
17477         
17478         this.indicator = this.indicatorEl();
17479         
17480         this.store = Roo.factory(this.store, Roo.data);
17481         this.store.parent = this;
17482         
17483         // if we are building from html. then this element is so complex, that we can not really
17484         // use the rendered HTML.
17485         // so we have to trash and replace the previous code.
17486         if (Roo.XComponent.build_from_html) {
17487             // remove this element....
17488             var e = this.el.dom, k=0;
17489             while (e ) { e = e.previousSibling;  ++k;}
17490
17491             this.el.remove();
17492             
17493             this.el=false;
17494             this.rendered = false;
17495             
17496             this.render(this.parent().getChildContainer(true), k);
17497         }
17498         
17499         if(Roo.isIOS && this.useNativeIOS){
17500             this.initIOSView();
17501             return;
17502         }
17503         
17504         /*
17505          * Touch Devices
17506          */
17507         
17508         if(Roo.isTouch && this.mobileTouchView){
17509             this.initTouchView();
17510             return;
17511         }
17512         
17513         if(this.tickable){
17514             this.initTickableEvents();
17515             return;
17516         }
17517         
17518         Roo.bootstrap.form.ComboBox.superclass.initEvents.call(this);
17519         
17520         if(this.hiddenName){
17521             
17522             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
17523             
17524             this.hiddenField.dom.value =
17525                 this.hiddenValue !== undefined ? this.hiddenValue :
17526                 this.value !== undefined ? this.value : '';
17527
17528             // prevent input submission
17529             this.el.dom.removeAttribute('name');
17530             this.hiddenField.dom.setAttribute('name', this.hiddenName);
17531              
17532              
17533         }
17534         //if(Roo.isGecko){
17535         //    this.el.dom.setAttribute('autocomplete', 'off');
17536         //}
17537         
17538         var cls = 'x-combo-list';
17539         
17540         //this.list = new Roo.Layer({
17541         //    shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false
17542         //});
17543         
17544         var _this = this;
17545         
17546         (function(){
17547             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
17548             _this.list.setWidth(lw);
17549         }).defer(100);
17550         
17551         this.list.on('mouseover', this.onViewOver, this);
17552         this.list.on('mousemove', this.onViewMove, this);
17553         this.list.on('scroll', this.onViewScroll, this);
17554         
17555         /*
17556         this.list.swallowEvent('mousewheel');
17557         this.assetHeight = 0;
17558
17559         if(this.title){
17560             this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
17561             this.assetHeight += this.header.getHeight();
17562         }
17563
17564         this.innerList = this.list.createChild({cls:cls+'-inner'});
17565         this.innerList.on('mouseover', this.onViewOver, this);
17566         this.innerList.on('mousemove', this.onViewMove, this);
17567         this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
17568         
17569         if(this.allowBlank && !this.pageSize && !this.disableClear){
17570             this.footer = this.list.createChild({cls:cls+'-ft'});
17571             this.pageTb = new Roo.Toolbar(this.footer);
17572            
17573         }
17574         if(this.pageSize){
17575             this.footer = this.list.createChild({cls:cls+'-ft'});
17576             this.pageTb = new Roo.PagingToolbar(this.footer, this.store,
17577                     {pageSize: this.pageSize});
17578             
17579         }
17580         
17581         if (this.pageTb && this.allowBlank && !this.disableClear) {
17582             var _this = this;
17583             this.pageTb.add(new Roo.Toolbar.Fill(), {
17584                 cls: 'x-btn-icon x-btn-clear',
17585                 text: '&#160;',
17586                 handler: function()
17587                 {
17588                     _this.collapse();
17589                     _this.clearValue();
17590                     _this.onSelect(false, -1);
17591                 }
17592             });
17593         }
17594         if (this.footer) {
17595             this.assetHeight += this.footer.getHeight();
17596         }
17597         */
17598             
17599         if(!this.tpl){
17600             this.tpl = Roo.bootstrap.version == 4 ?
17601                 '<a class="dropdown-item" href="#">{' + this.displayField + '}</a>' :  // 4 does not need <li> and it get's really confisued.
17602                 '<li><a class="dropdown-item" href="#">{' + this.displayField + '}</a></li>';
17603         }
17604
17605         this.view = new Roo.View(this.list, this.tpl, {
17606             singleSelect:true, store: this.store, selectedClass: this.selectedClass
17607         });
17608         //this.view.wrapEl.setDisplayed(false);
17609         this.view.on('click', this.onViewClick, this);
17610         
17611         
17612         this.store.on('beforeload', this.onBeforeLoad, this);
17613         this.store.on('load', this.onLoad, this);
17614         this.store.on('loadexception', this.onLoadException, this);
17615         /*
17616         if(this.resizable){
17617             this.resizer = new Roo.Resizable(this.list,  {
17618                pinned:true, handles:'se'
17619             });
17620             this.resizer.on('resize', function(r, w, h){
17621                 this.maxHeight = h-this.handleHeight-this.list.getFrameWidth('tb')-this.assetHeight;
17622                 this.listWidth = w;
17623                 this.innerList.setWidth(w - this.list.getFrameWidth('lr'));
17624                 this.restrictHeight();
17625             }, this);
17626             this[this.pageSize?'footer':'innerList'].setStyle('margin-bottom', this.handleHeight+'px');
17627         }
17628         */
17629         if(!this.editable){
17630             this.editable = true;
17631             this.setEditable(false);
17632         }
17633         
17634         /*
17635         
17636         if (typeof(this.events.add.listeners) != 'undefined') {
17637             
17638             this.addicon = this.wrap.createChild(
17639                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-add' });  
17640        
17641             this.addicon.on('click', function(e) {
17642                 this.fireEvent('add', this);
17643             }, this);
17644         }
17645         if (typeof(this.events.edit.listeners) != 'undefined') {
17646             
17647             this.editicon = this.wrap.createChild(
17648                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-edit' });  
17649             if (this.addicon) {
17650                 this.editicon.setStyle('margin-left', '40px');
17651             }
17652             this.editicon.on('click', function(e) {
17653                 
17654                 // we fire even  if inothing is selected..
17655                 this.fireEvent('edit', this, this.lastData );
17656                 
17657             }, this);
17658         }
17659         */
17660         
17661         this.keyNav = new Roo.KeyNav(this.inputEl(), {
17662             "up" : function(e){
17663                 this.inKeyMode = true;
17664                 this.selectPrev();
17665             },
17666
17667             "down" : function(e){
17668                 if(!this.isExpanded()){
17669                     this.onTriggerClick();
17670                 }else{
17671                     this.inKeyMode = true;
17672                     this.selectNext();
17673                 }
17674             },
17675
17676             "enter" : function(e){
17677 //                this.onViewClick();
17678                 //return true;
17679                 this.collapse();
17680                 
17681                 if(this.fireEvent("specialkey", this, e)){
17682                     this.onViewClick(false);
17683                 }
17684                 
17685                 return true;
17686             },
17687
17688             "esc" : function(e){
17689                 this.collapse();
17690             },
17691
17692             "tab" : function(e){
17693                 this.collapse();
17694                 
17695                 if(this.fireEvent("specialkey", this, e)){
17696                     this.onViewClick(false);
17697                 }
17698                 
17699                 return true;
17700             },
17701
17702             scope : this,
17703
17704             doRelay : function(foo, bar, hname){
17705                 if(hname == 'down' || this.scope.isExpanded()){
17706                    return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
17707                 }
17708                 return true;
17709             },
17710
17711             forceKeyDown: true
17712         });
17713         
17714         
17715         this.queryDelay = Math.max(this.queryDelay || 10,
17716                 this.mode == 'local' ? 10 : 250);
17717         
17718         
17719         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
17720         
17721         if(this.typeAhead){
17722             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
17723         }
17724         if(this.editable !== false){
17725             this.inputEl().on("keyup", this.onKeyUp, this);
17726         }
17727         if(this.forceSelection){
17728             this.inputEl().on('blur', this.doForce, this);
17729         }
17730         
17731         if(this.multiple){
17732             this.choices = this.el.select('ul.roo-select2-choices', true).first();
17733             this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
17734         }
17735     },
17736     
17737     initTickableEvents: function()
17738     {   
17739         this.createList();
17740         
17741         if(this.hiddenName){
17742             
17743             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
17744             
17745             this.hiddenField.dom.value =
17746                 this.hiddenValue !== undefined ? this.hiddenValue :
17747                 this.value !== undefined ? this.value : '';
17748
17749             // prevent input submission
17750             this.el.dom.removeAttribute('name');
17751             this.hiddenField.dom.setAttribute('name', this.hiddenName);
17752              
17753              
17754         }
17755         
17756 //        this.list = this.el.select('ul.dropdown-menu',true).first();
17757         
17758         this.choices = this.el.select('ul.roo-select2-choices', true).first();
17759         this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
17760         if(this.triggerList){
17761             this.searchField.on("click", this.onSearchFieldClick, this, {preventDefault:true});
17762         }
17763          
17764         this.trigger = this.el.select('.tickable-buttons > .btn-edit', true).first();
17765         this.trigger.on("click", this.onTickableTriggerClick, this, {preventDefault:true});
17766         
17767         this.okBtn = this.el.select('.tickable-buttons > .btn-ok', true).first();
17768         this.cancelBtn = this.el.select('.tickable-buttons > .btn-cancel', true).first();
17769         
17770         this.okBtn.on('click', this.onTickableFooterButtonClick, this, this.okBtn);
17771         this.cancelBtn.on('click', this.onTickableFooterButtonClick, this, this.cancelBtn);
17772         
17773         this.trigger.setVisibilityMode(Roo.Element.DISPLAY);
17774         this.okBtn.setVisibilityMode(Roo.Element.DISPLAY);
17775         this.cancelBtn.setVisibilityMode(Roo.Element.DISPLAY);
17776         
17777         this.okBtn.hide();
17778         this.cancelBtn.hide();
17779         
17780         var _this = this;
17781         
17782         (function(){
17783             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
17784             _this.list.setWidth(lw);
17785         }).defer(100);
17786         
17787         this.list.on('mouseover', this.onViewOver, this);
17788         this.list.on('mousemove', this.onViewMove, this);
17789         
17790         this.list.on('scroll', this.onViewScroll, this);
17791         
17792         if(!this.tpl){
17793             this.tpl = '<li class="roo-select2-result"><div class="checkbox"><input id="{roo-id}"' + 
17794                 'type="checkbox" {roo-data-checked}><label for="{roo-id}"><b>{' + this.displayField + '}</b></label></div></li>';
17795         }
17796
17797         this.view = new Roo.View(this.list, this.tpl, {
17798             singleSelect:true,
17799             tickable:true,
17800             parent:this,
17801             store: this.store,
17802             selectedClass: this.selectedClass
17803         });
17804         
17805         //this.view.wrapEl.setDisplayed(false);
17806         this.view.on('click', this.onViewClick, this);
17807         
17808         
17809         
17810         this.store.on('beforeload', this.onBeforeLoad, this);
17811         this.store.on('load', this.onLoad, this);
17812         this.store.on('loadexception', this.onLoadException, this);
17813         
17814         if(this.editable){
17815             this.keyNav = new Roo.KeyNav(this.tickableInputEl(), {
17816                 "up" : function(e){
17817                     this.inKeyMode = true;
17818                     this.selectPrev();
17819                 },
17820
17821                 "down" : function(e){
17822                     this.inKeyMode = true;
17823                     this.selectNext();
17824                 },
17825
17826                 "enter" : function(e){
17827                     if(this.fireEvent("specialkey", this, e)){
17828                         this.onViewClick(false);
17829                     }
17830                     
17831                     return true;
17832                 },
17833
17834                 "esc" : function(e){
17835                     this.onTickableFooterButtonClick(e, false, false);
17836                 },
17837
17838                 "tab" : function(e){
17839                     this.fireEvent("specialkey", this, e);
17840                     
17841                     this.onTickableFooterButtonClick(e, false, false);
17842                     
17843                     return true;
17844                 },
17845
17846                 scope : this,
17847
17848                 doRelay : function(e, fn, key){
17849                     if(this.scope.isExpanded()){
17850                        return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
17851                     }
17852                     return true;
17853                 },
17854
17855                 forceKeyDown: true
17856             });
17857         }
17858         
17859         this.queryDelay = Math.max(this.queryDelay || 10,
17860                 this.mode == 'local' ? 10 : 250);
17861         
17862         
17863         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
17864         
17865         if(this.typeAhead){
17866             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
17867         }
17868         
17869         if(this.editable !== false){
17870             this.tickableInputEl().on("keyup", this.onKeyUp, this);
17871         }
17872         
17873         this.indicator = this.indicatorEl();
17874         
17875         if(this.indicator){
17876             this.indicator.setVisibilityMode(Roo.Element.DISPLAY);
17877             this.indicator.hide();
17878         }
17879         
17880     },
17881
17882     onDestroy : function(){
17883         if(this.view){
17884             this.view.setStore(null);
17885             this.view.el.removeAllListeners();
17886             this.view.el.remove();
17887             this.view.purgeListeners();
17888         }
17889         if(this.list){
17890             this.list.dom.innerHTML  = '';
17891         }
17892         
17893         if(this.store){
17894             this.store.un('beforeload', this.onBeforeLoad, this);
17895             this.store.un('load', this.onLoad, this);
17896             this.store.un('loadexception', this.onLoadException, this);
17897         }
17898         Roo.bootstrap.form.ComboBox.superclass.onDestroy.call(this);
17899     },
17900
17901     // private
17902     fireKey : function(e){
17903         if(e.isNavKeyPress() && !this.list.isVisible()){
17904             this.fireEvent("specialkey", this, e);
17905         }
17906     },
17907
17908     // private
17909     onResize: function(w, h)
17910     {
17911         
17912         
17913 //        Roo.bootstrap.form.ComboBox.superclass.onResize.apply(this, arguments);
17914 //        
17915 //        if(typeof w != 'number'){
17916 //            // we do not handle it!?!?
17917 //            return;
17918 //        }
17919 //        var tw = this.trigger.getWidth();
17920 //       // tw += this.addicon ? this.addicon.getWidth() : 0;
17921 //       // tw += this.editicon ? this.editicon.getWidth() : 0;
17922 //        var x = w - tw;
17923 //        this.inputEl().setWidth( this.adjustWidth('input', x));
17924 //            
17925 //        //this.trigger.setStyle('left', x+'px');
17926 //        
17927 //        if(this.list && this.listWidth === undefined){
17928 //            var lw = Math.max(x + this.trigger.getWidth(), this.minListWidth);
17929 //            this.list.setWidth(lw);
17930 //            this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
17931 //        }
17932         
17933     
17934         
17935     },
17936
17937     /**
17938      * Allow or prevent the user from directly editing the field text.  If false is passed,
17939      * the user will only be able to select from the items defined in the dropdown list.  This method
17940      * is the runtime equivalent of setting the 'editable' config option at config time.
17941      * @param {Boolean} value True to allow the user to directly edit the field text
17942      */
17943     setEditable : function(value){
17944         if(value == this.editable){
17945             return;
17946         }
17947         this.editable = value;
17948         if(!value){
17949             this.inputEl().dom.setAttribute('readOnly', true);
17950             this.inputEl().on('mousedown', this.onTriggerClick,  this);
17951             this.inputEl().addClass('x-combo-noedit');
17952         }else{
17953             this.inputEl().dom.removeAttribute('readOnly');
17954             this.inputEl().un('mousedown', this.onTriggerClick,  this);
17955             this.inputEl().removeClass('x-combo-noedit');
17956         }
17957     },
17958
17959     // private
17960     
17961     onBeforeLoad : function(combo,opts){
17962         if(!this.hasFocus){
17963             return;
17964         }
17965          if (!opts.add) {
17966             this.list.dom.innerHTML = '<li class="loading-indicator">'+(this.loadingText||'loading')+'</li>' ;
17967          }
17968         this.restrictHeight();
17969         this.selectedIndex = -1;
17970     },
17971
17972     // private
17973     onLoad : function(){
17974         
17975         this.hasQuery = false;
17976         
17977         if(!this.hasFocus){
17978             return;
17979         }
17980         
17981         if(typeof(this.loading) !== 'undefined' && this.loading !== null){
17982             this.loading.hide();
17983         }
17984         
17985         if(this.store.getCount() > 0){
17986             
17987             this.expand();
17988             this.restrictHeight();
17989             if(this.lastQuery == this.allQuery){
17990                 if(this.editable && !this.tickable){
17991                     this.inputEl().dom.select();
17992                 }
17993                 
17994                 if(
17995                     !this.selectByValue(this.value, true) &&
17996                     this.autoFocus && 
17997                     (
17998                         !this.store.lastOptions ||
17999                         typeof(this.store.lastOptions.add) == 'undefined' || 
18000                         this.store.lastOptions.add != true
18001                     )
18002                 ){
18003                     this.select(0, true);
18004                 }
18005             }else{
18006                 if(this.autoFocus){
18007                     this.selectNext();
18008                 }
18009                 if(this.typeAhead && this.lastKey != Roo.EventObject.BACKSPACE && this.lastKey != Roo.EventObject.DELETE){
18010                     this.taTask.delay(this.typeAheadDelay);
18011                 }
18012             }
18013         }else{
18014             this.onEmptyResults();
18015         }
18016         
18017         //this.el.focus();
18018     },
18019     // private
18020     onLoadException : function()
18021     {
18022         this.hasQuery = false;
18023         
18024         if(typeof(this.loading) !== 'undefined' && this.loading !== null){
18025             this.loading.hide();
18026         }
18027         
18028         if(this.tickable && this.editable){
18029             return;
18030         }
18031         
18032         this.collapse();
18033         // only causes errors at present
18034         //Roo.log(this.store.reader.jsonData);
18035         //if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
18036             // fixme
18037             //Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
18038         //}
18039         
18040         
18041     },
18042     // private
18043     onTypeAhead : function(){
18044         if(this.store.getCount() > 0){
18045             var r = this.store.getAt(0);
18046             var newValue = r.data[this.displayField];
18047             var len = newValue.length;
18048             var selStart = this.getRawValue().length;
18049             
18050             if(selStart != len){
18051                 this.setRawValue(newValue);
18052                 this.selectText(selStart, newValue.length);
18053             }
18054         }
18055     },
18056
18057     // private
18058     onSelect : function(record, index){
18059         
18060         if(this.fireEvent('beforeselect', this, record, index) !== false){
18061         
18062             this.setFromData(index > -1 ? record.data : false);
18063             
18064             this.collapse();
18065             this.fireEvent('select', this, record, index);
18066         }
18067     },
18068
18069     /**
18070      * Returns the currently selected field value or empty string if no value is set.
18071      * @return {String} value The selected value
18072      */
18073     getValue : function()
18074     {
18075         if(Roo.isIOS && this.useNativeIOS){
18076             return this.ios_options[this.inputEl().dom.selectedIndex].data[this.valueField];
18077         }
18078         
18079         if(this.multiple){
18080             return (this.hiddenField) ? this.hiddenField.dom.value : this.value;
18081         }
18082         
18083         if(this.valueField){
18084             return typeof this.value != 'undefined' ? this.value : '';
18085         }else{
18086             return Roo.bootstrap.form.ComboBox.superclass.getValue.call(this);
18087         }
18088     },
18089     
18090     getRawValue : function()
18091     {
18092         if(Roo.isIOS && this.useNativeIOS){
18093             return this.ios_options[this.inputEl().dom.selectedIndex].data[this.displayField];
18094         }
18095         
18096         var v = this.inputEl().getValue();
18097         
18098         return v;
18099     },
18100
18101     /**
18102      * Clears any text/value currently set in the field
18103      */
18104     clearValue : function(){
18105         
18106         if(this.hiddenField){
18107             this.hiddenField.dom.value = '';
18108         }
18109         this.value = '';
18110         this.setRawValue('');
18111         this.lastSelectionText = '';
18112         this.lastData = false;
18113         
18114         var close = this.closeTriggerEl();
18115         
18116         if(close){
18117             close.hide();
18118         }
18119         
18120         this.validate();
18121         
18122     },
18123
18124     /**
18125      * Sets the specified value into the field.  If the value finds a match, the corresponding record text
18126      * will be displayed in the field.  If the value does not match the data value of an existing item,
18127      * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
18128      * Otherwise the field will be blank (although the value will still be set).
18129      * @param {String} value The value to match
18130      */
18131     setValue : function(v)
18132     {
18133         if(Roo.isIOS && this.useNativeIOS){
18134             this.setIOSValue(v);
18135             return;
18136         }
18137         
18138         if(this.multiple){
18139             this.syncValue();
18140             return;
18141         }
18142         
18143         var text = v;
18144         if(this.valueField){
18145             var r = this.findRecord(this.valueField, v);
18146             if(r){
18147                 text = r.data[this.displayField];
18148             }else if(this.valueNotFoundText !== undefined){
18149                 text = this.valueNotFoundText;
18150             }
18151         }
18152         this.lastSelectionText = text;
18153         if(this.hiddenField){
18154             this.hiddenField.dom.value = v;
18155         }
18156         Roo.bootstrap.form.ComboBox.superclass.setValue.call(this, text);
18157         this.value = v;
18158         
18159         var close = this.closeTriggerEl();
18160         
18161         if(close){
18162             (v && (v.length || v * 1 > 0)) ? close.show() : close.hide();
18163         }
18164         
18165         this.validate();
18166     },
18167     /**
18168      * @property {Object} the last set data for the element
18169      */
18170     
18171     lastData : false,
18172     /**
18173      * Sets the value of the field based on a object which is related to the record format for the store.
18174      * @param {Object} value the value to set as. or false on reset?
18175      */
18176     setFromData : function(o){
18177         
18178         if(this.multiple){
18179             this.addItem(o);
18180             return;
18181         }
18182             
18183         var dv = ''; // display value
18184         var vv = ''; // value value..
18185         this.lastData = o;
18186         if (this.displayField) {
18187             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
18188         } else {
18189             // this is an error condition!!!
18190             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
18191         }
18192         
18193         if(this.valueField){
18194             vv = !o || typeof(o[this.valueField]) == 'undefined' ? dv : o[this.valueField];
18195         }
18196         
18197         var close = this.closeTriggerEl();
18198         
18199         if(close){
18200             if(dv.length || vv * 1 > 0){
18201                 close.show() ;
18202                 this.blockFocus=true;
18203             } else {
18204                 close.hide();
18205             }             
18206         }
18207         
18208         if(this.hiddenField){
18209             this.hiddenField.dom.value = vv;
18210             
18211             this.lastSelectionText = dv;
18212             Roo.bootstrap.form.ComboBox.superclass.setValue.call(this, dv);
18213             this.value = vv;
18214             return;
18215         }
18216         // no hidden field.. - we store the value in 'value', but still display
18217         // display field!!!!
18218         this.lastSelectionText = dv;
18219         Roo.bootstrap.form.ComboBox.superclass.setValue.call(this, dv);
18220         this.value = vv;
18221         
18222         
18223         
18224     },
18225     // private
18226     reset : function(){
18227         // overridden so that last data is reset..
18228         
18229         if(this.multiple){
18230             this.clearItem();
18231             return;
18232         }
18233         
18234         this.setValue(this.originalValue);
18235         //this.clearInvalid();
18236         this.lastData = false;
18237         if (this.view) {
18238             this.view.clearSelections();
18239         }
18240         
18241         this.validate();
18242     },
18243     // private
18244     findRecord : function(prop, value){
18245         var record;
18246         if(this.store.getCount() > 0){
18247             this.store.each(function(r){
18248                 if(r.data[prop] == value){
18249                     record = r;
18250                     return false;
18251                 }
18252                 return true;
18253             });
18254         }
18255         return record;
18256     },
18257     
18258     getName: function()
18259     {
18260         // returns hidden if it's set..
18261         if (!this.rendered) {return ''};
18262         return !this.hiddenName && this.inputEl().dom.name  ? this.inputEl().dom.name : (this.hiddenName || '');
18263         
18264     },
18265     // private
18266     onViewMove : function(e, t){
18267         this.inKeyMode = false;
18268     },
18269
18270     // private
18271     onViewOver : function(e, t){
18272         if(this.inKeyMode){ // prevent key nav and mouse over conflicts
18273             return;
18274         }
18275         var item = this.view.findItemFromChild(t);
18276         
18277         if(item){
18278             var index = this.view.indexOf(item);
18279             this.select(index, false);
18280         }
18281     },
18282
18283     // private
18284     onViewClick : function(view, doFocus, el, e)
18285     {
18286         var index = this.view.getSelectedIndexes()[0];
18287         
18288         var r = this.store.getAt(index);
18289         
18290         if(this.tickable){
18291             
18292             if(typeof(e) != 'undefined' && e.getTarget().nodeName.toLowerCase() != 'input'){
18293                 return;
18294             }
18295             
18296             var rm = false;
18297             var _this = this;
18298             
18299             Roo.each(this.tickItems, function(v,k){
18300                 
18301                 if(typeof(v) != 'undefined' && v[_this.valueField] == r.data[_this.valueField]){
18302                     Roo.log(v);
18303                     _this.tickItems.splice(k, 1);
18304                     
18305                     if(typeof(e) == 'undefined' && view == false){
18306                         Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = false;
18307                     }
18308                     
18309                     rm = true;
18310                     return;
18311                 }
18312             });
18313             
18314             if(rm){
18315                 return;
18316             }
18317             
18318             if(this.fireEvent('tick', this, r, index, Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked) !== false){
18319                 this.tickItems.push(r.data);
18320             }
18321             
18322             if(typeof(e) == 'undefined' && view == false){
18323                 Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = true;
18324             }
18325                     
18326             return;
18327         }
18328         
18329         if(r){
18330             this.onSelect(r, index);
18331         }
18332         if(doFocus !== false && !this.blockFocus){
18333             this.inputEl().focus();
18334         }
18335     },
18336
18337     // private
18338     restrictHeight : function(){
18339         //this.innerList.dom.style.height = '';
18340         //var inner = this.innerList.dom;
18341         //var h = Math.max(inner.clientHeight, inner.offsetHeight, inner.scrollHeight);
18342         //this.innerList.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
18343         //this.list.beginUpdate();
18344         //this.list.setHeight(this.innerList.getHeight()+this.list.getFrameWidth('tb')+(this.resizable?this.handleHeight:0)+this.assetHeight);
18345         this.list.alignTo(this.inputEl(), this.listAlign);
18346         this.list.alignTo(this.inputEl(), this.listAlign);
18347         //this.list.endUpdate();
18348     },
18349
18350     // private
18351     onEmptyResults : function(){
18352         
18353         if(this.tickable && this.editable){
18354             this.hasFocus = false;
18355             this.restrictHeight();
18356             return;
18357         }
18358         
18359         this.collapse();
18360     },
18361
18362     /**
18363      * Returns true if the dropdown list is expanded, else false.
18364      */
18365     isExpanded : function(){
18366         return this.list.isVisible();
18367     },
18368
18369     /**
18370      * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
18371      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
18372      * @param {String} value The data value of the item to select
18373      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
18374      * selected item if it is not currently in view (defaults to true)
18375      * @return {Boolean} True if the value matched an item in the list, else false
18376      */
18377     selectByValue : function(v, scrollIntoView){
18378         if(v !== undefined && v !== null){
18379             var r = this.findRecord(this.valueField || this.displayField, v);
18380             if(r){
18381                 this.select(this.store.indexOf(r), scrollIntoView);
18382                 return true;
18383             }
18384         }
18385         return false;
18386     },
18387
18388     /**
18389      * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
18390      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
18391      * @param {Number} index The zero-based index of the list item to select
18392      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
18393      * selected item if it is not currently in view (defaults to true)
18394      */
18395     select : function(index, scrollIntoView){
18396         this.selectedIndex = index;
18397         this.view.select(index);
18398         if(scrollIntoView !== false){
18399             var el = this.view.getNode(index);
18400             /*
18401              * el && !this.multiple && !this.tickable // not sure why we disable multiple before..
18402              */
18403             if(el){
18404                 this.list.scrollChildIntoView(el, false);
18405             }
18406         }
18407     },
18408
18409     // private
18410     selectNext : function(){
18411         var ct = this.store.getCount();
18412         if(ct > 0){
18413             if(this.selectedIndex == -1){
18414                 this.select(0);
18415             }else if(this.selectedIndex < ct-1){
18416                 this.select(this.selectedIndex+1);
18417             }
18418         }
18419     },
18420
18421     // private
18422     selectPrev : function(){
18423         var ct = this.store.getCount();
18424         if(ct > 0){
18425             if(this.selectedIndex == -1){
18426                 this.select(0);
18427             }else if(this.selectedIndex != 0){
18428                 this.select(this.selectedIndex-1);
18429             }
18430         }
18431     },
18432
18433     // private
18434     onKeyUp : function(e){
18435         if(this.editable !== false && !e.isSpecialKey()){
18436             this.lastKey = e.getKey();
18437             this.dqTask.delay(this.queryDelay);
18438         }
18439     },
18440
18441     // private
18442     validateBlur : function(){
18443         return !this.list || !this.list.isVisible();   
18444     },
18445
18446     // private
18447     initQuery : function(){
18448         
18449         var v = this.getRawValue();
18450         
18451         if(this.tickable && this.editable){
18452             v = this.tickableInputEl().getValue();
18453         }
18454         
18455         this.doQuery(v);
18456     },
18457
18458     // private
18459     doForce : function(){
18460         if(this.inputEl().dom.value.length > 0){
18461             this.inputEl().dom.value =
18462                 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
18463              
18464         }
18465     },
18466
18467     /**
18468      * Execute a query to filter the dropdown list.  Fires the beforequery event prior to performing the
18469      * query allowing the query action to be canceled if needed.
18470      * @param {String} query The SQL query to execute
18471      * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
18472      * in the field than the minimum specified by the minChars config option.  It also clears any filter previously
18473      * saved in the current store (defaults to false)
18474      */
18475     doQuery : function(q, forceAll){
18476         
18477         if(q === undefined || q === null){
18478             q = '';
18479         }
18480         var qe = {
18481             query: q,
18482             forceAll: forceAll,
18483             combo: this,
18484             cancel:false
18485         };
18486         if(this.fireEvent('beforequery', qe)===false || qe.cancel){
18487             return false;
18488         }
18489         q = qe.query;
18490         
18491         forceAll = qe.forceAll;
18492         if(forceAll === true || (q.length >= this.minChars)){
18493             
18494             this.hasQuery = true;
18495             
18496             if(this.lastQuery != q || this.alwaysQuery){
18497                 this.lastQuery = q;
18498                 if(this.mode == 'local'){
18499                     this.selectedIndex = -1;
18500                     if(forceAll){
18501                         this.store.clearFilter();
18502                     }else{
18503                         
18504                         if(this.specialFilter){
18505                             this.fireEvent('specialfilter', this);
18506                             this.onLoad();
18507                             return;
18508                         }
18509                         
18510                         this.store.filter(this.displayField, q);
18511                     }
18512                     
18513                     this.store.fireEvent("datachanged", this.store);
18514                     
18515                     this.onLoad();
18516                     
18517                     
18518                 }else{
18519                     
18520                     this.store.baseParams[this.queryParam] = q;
18521                     
18522                     var options = {params : this.getParams(q)};
18523                     
18524                     if(this.loadNext){
18525                         options.add = true;
18526                         options.params.start = this.page * this.pageSize;
18527                     }
18528                     
18529                     this.store.load(options);
18530                     
18531                     /*
18532                      *  this code will make the page width larger, at the beginning, the list not align correctly, 
18533                      *  we should expand the list on onLoad
18534                      *  so command out it
18535                      */
18536 //                    this.expand();
18537                 }
18538             }else{
18539                 this.selectedIndex = -1;
18540                 this.onLoad();   
18541             }
18542         }
18543         
18544         this.loadNext = false;
18545     },
18546     
18547     // private
18548     getParams : function(q){
18549         var p = {};
18550         //p[this.queryParam] = q;
18551         
18552         if(this.pageSize){
18553             p.start = 0;
18554             p.limit = this.pageSize;
18555         }
18556         return p;
18557     },
18558
18559     /**
18560      * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
18561      */
18562     collapse : function(){
18563         if(!this.isExpanded()){
18564             return;
18565         }
18566         
18567         this.list.hide();
18568         
18569         this.hasFocus = false;
18570         
18571         if(this.tickable){
18572             this.okBtn.hide();
18573             this.cancelBtn.hide();
18574             this.trigger.show();
18575             
18576             if(this.editable){
18577                 this.tickableInputEl().dom.value = '';
18578                 this.tickableInputEl().blur();
18579             }
18580             
18581         }
18582         
18583         Roo.get(document).un('mousedown', this.collapseIf, this);
18584         Roo.get(document).un('mousewheel', this.collapseIf, this);
18585         if (!this.editable) {
18586             Roo.get(document).un('keydown', this.listKeyPress, this);
18587         }
18588         this.fireEvent('collapse', this);
18589         
18590         this.validate();
18591     },
18592
18593     // private
18594     collapseIf : function(e){
18595         var in_combo  = e.within(this.el);
18596         var in_list =  e.within(this.list);
18597         var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
18598         
18599         if (in_combo || in_list || is_list) {
18600             //e.stopPropagation();
18601             return;
18602         }
18603         
18604         if(this.tickable){
18605             this.onTickableFooterButtonClick(e, false, false);
18606         }
18607
18608         this.collapse();
18609         
18610     },
18611
18612     /**
18613      * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
18614      */
18615     expand : function(){
18616        
18617         if(this.isExpanded() || !this.hasFocus){
18618             return;
18619         }
18620         
18621         var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
18622         this.list.setWidth(lw);
18623         
18624         Roo.log('expand');
18625         
18626         this.list.show();
18627         
18628         this.restrictHeight();
18629         
18630         if(this.tickable){
18631             
18632             this.tickItems = Roo.apply([], this.item);
18633             
18634             this.okBtn.show();
18635             this.cancelBtn.show();
18636             this.trigger.hide();
18637             
18638             if(this.editable){
18639                 this.tickableInputEl().focus();
18640             }
18641             
18642         }
18643         
18644         Roo.get(document).on('mousedown', this.collapseIf, this);
18645         Roo.get(document).on('mousewheel', this.collapseIf, this);
18646         if (!this.editable) {
18647             Roo.get(document).on('keydown', this.listKeyPress, this);
18648         }
18649         
18650         this.fireEvent('expand', this);
18651     },
18652
18653     // private
18654     // Implements the default empty TriggerField.onTriggerClick function
18655     onTriggerClick : function(e)
18656     {
18657         Roo.log('trigger click');
18658         
18659         if(this.disabled || !this.triggerList){
18660             return;
18661         }
18662         
18663         this.page = 0;
18664         this.loadNext = false;
18665         
18666         if(this.isExpanded()){
18667             this.collapse();
18668             if (!this.blockFocus) {
18669                 this.inputEl().focus();
18670             }
18671             
18672         }else {
18673             this.hasFocus = true;
18674             if(this.triggerAction == 'all') {
18675                 this.doQuery(this.allQuery, true);
18676             } else {
18677                 this.doQuery(this.getRawValue());
18678             }
18679             if (!this.blockFocus) {
18680                 this.inputEl().focus();
18681             }
18682         }
18683     },
18684     
18685     onTickableTriggerClick : function(e)
18686     {
18687         if(this.disabled){
18688             return;
18689         }
18690         
18691         this.page = 0;
18692         this.loadNext = false;
18693         this.hasFocus = true;
18694         
18695         if(this.triggerAction == 'all') {
18696             this.doQuery(this.allQuery, true);
18697         } else {
18698             this.doQuery(this.getRawValue());
18699         }
18700     },
18701     
18702     onSearchFieldClick : function(e)
18703     {
18704         if(this.hasFocus && !this.disabled && e.getTarget().nodeName.toLowerCase() != 'button'){
18705             this.onTickableFooterButtonClick(e, false, false);
18706             return;
18707         }
18708         
18709         if(this.hasFocus || this.disabled || e.getTarget().nodeName.toLowerCase() == 'button'){
18710             return;
18711         }
18712         
18713         this.page = 0;
18714         this.loadNext = false;
18715         this.hasFocus = true;
18716         
18717         if(this.triggerAction == 'all') {
18718             this.doQuery(this.allQuery, true);
18719         } else {
18720             this.doQuery(this.getRawValue());
18721         }
18722     },
18723     
18724     listKeyPress : function(e)
18725     {
18726         //Roo.log('listkeypress');
18727         // scroll to first matching element based on key pres..
18728         if (e.isSpecialKey()) {
18729             return false;
18730         }
18731         var k = String.fromCharCode(e.getKey()).toUpperCase();
18732         //Roo.log(k);
18733         var match  = false;
18734         var csel = this.view.getSelectedNodes();
18735         var cselitem = false;
18736         if (csel.length) {
18737             var ix = this.view.indexOf(csel[0]);
18738             cselitem  = this.store.getAt(ix);
18739             if (!cselitem.get(this.displayField) || cselitem.get(this.displayField).substring(0,1).toUpperCase() != k) {
18740                 cselitem = false;
18741             }
18742             
18743         }
18744         
18745         this.store.each(function(v) { 
18746             if (cselitem) {
18747                 // start at existing selection.
18748                 if (cselitem.id == v.id) {
18749                     cselitem = false;
18750                 }
18751                 return true;
18752             }
18753                 
18754             if (v.get(this.displayField) && v.get(this.displayField).substring(0,1).toUpperCase() == k) {
18755                 match = this.store.indexOf(v);
18756                 return false;
18757             }
18758             return true;
18759         }, this);
18760         
18761         if (match === false) {
18762             return true; // no more action?
18763         }
18764         // scroll to?
18765         this.view.select(match);
18766         var sn = Roo.get(this.view.getSelectedNodes()[0]);
18767         sn.scrollIntoView(sn.dom.parentNode, false);
18768     },
18769     
18770     onViewScroll : function(e, t){
18771         
18772         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){
18773             return;
18774         }
18775         
18776         this.hasQuery = true;
18777         
18778         this.loading = this.list.select('.loading', true).first();
18779         
18780         if(this.loading === null){
18781             this.list.createChild({
18782                 tag: 'div',
18783                 cls: 'loading roo-select2-more-results roo-select2-active',
18784                 html: 'Loading more results...'
18785             });
18786             
18787             this.loading = this.list.select('.loading', true).first();
18788             
18789             this.loading.setVisibilityMode(Roo.Element.DISPLAY);
18790             
18791             this.loading.hide();
18792         }
18793         
18794         this.loading.show();
18795         
18796         var _combo = this;
18797         
18798         this.page++;
18799         this.loadNext = true;
18800         
18801         (function() { _combo.doQuery(_combo.allQuery, true); }).defer(500);
18802         
18803         return;
18804     },
18805     
18806     addItem : function(o)
18807     {   
18808         var dv = ''; // display value
18809         
18810         if (this.displayField) {
18811             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
18812         } else {
18813             // this is an error condition!!!
18814             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
18815         }
18816         
18817         if(!dv.length){
18818             return;
18819         }
18820         
18821         var choice = this.choices.createChild({
18822             tag: 'li',
18823             cls: 'roo-select2-search-choice',
18824             cn: [
18825                 {
18826                     tag: 'div',
18827                     html: dv
18828                 },
18829                 {
18830                     tag: 'a',
18831                     href: '#',
18832                     cls: 'roo-select2-search-choice-close fa fa-times',
18833                     tabindex: '-1'
18834                 }
18835             ]
18836             
18837         }, this.searchField);
18838         
18839         var close = choice.select('a.roo-select2-search-choice-close', true).first();
18840         
18841         close.on('click', this.onRemoveItem, this, { item : choice, data : o} );
18842         
18843         this.item.push(o);
18844         
18845         this.lastData = o;
18846         
18847         this.syncValue();
18848         
18849         this.inputEl().dom.value = '';
18850         
18851         this.validate();
18852     },
18853     
18854     onRemoveItem : function(e, _self, o)
18855     {
18856         e.preventDefault();
18857         
18858         this.lastItem = Roo.apply([], this.item);
18859         
18860         var index = this.item.indexOf(o.data) * 1;
18861         
18862         if( index < 0){
18863             Roo.log('not this item?!');
18864             return;
18865         }
18866         
18867         this.item.splice(index, 1);
18868         o.item.remove();
18869         
18870         this.syncValue();
18871         
18872         this.fireEvent('remove', this, e);
18873         
18874         this.validate();
18875         
18876     },
18877     
18878     syncValue : function()
18879     {
18880         if(!this.item.length){
18881             this.clearValue();
18882             return;
18883         }
18884             
18885         var value = [];
18886         var _this = this;
18887         Roo.each(this.item, function(i){
18888             if(_this.valueField){
18889                 value.push(i[_this.valueField]);
18890                 return;
18891             }
18892
18893             value.push(i);
18894         });
18895
18896         this.value = value.join(',');
18897
18898         if(this.hiddenField){
18899             this.hiddenField.dom.value = this.value;
18900         }
18901         
18902         this.store.fireEvent("datachanged", this.store);
18903         
18904         this.validate();
18905     },
18906     
18907     clearItem : function()
18908     {
18909         if(!this.multiple){
18910             return;
18911         }
18912         
18913         this.item = [];
18914         
18915         Roo.each(this.choices.select('>li.roo-select2-search-choice', true).elements, function(c){
18916            c.remove();
18917         });
18918         
18919         this.syncValue();
18920         
18921         this.validate();
18922         
18923         if(this.tickable && !Roo.isTouch){
18924             this.view.refresh();
18925         }
18926     },
18927     
18928     inputEl: function ()
18929     {
18930         if(Roo.isIOS && this.useNativeIOS){
18931             return this.el.select('select.roo-ios-select', true).first();
18932         }
18933         
18934         if(Roo.isTouch && this.mobileTouchView){
18935             return this.el.select('input.form-control',true).first();
18936         }
18937         
18938         if(this.tickable){
18939             return this.searchField;
18940         }
18941         
18942         return this.el.select('input.form-control',true).first();
18943     },
18944     
18945     onTickableFooterButtonClick : function(e, btn, el)
18946     {
18947         e.preventDefault();
18948         
18949         this.lastItem = Roo.apply([], this.item);
18950         
18951         if(btn && btn.name == 'cancel'){
18952             this.tickItems = Roo.apply([], this.item);
18953             this.collapse();
18954             return;
18955         }
18956         
18957         this.clearItem();
18958         
18959         var _this = this;
18960         
18961         Roo.each(this.tickItems, function(o){
18962             _this.addItem(o);
18963         });
18964         
18965         this.collapse();
18966         
18967     },
18968     
18969     validate : function()
18970     {
18971         if(this.getVisibilityEl().hasClass('hidden')){
18972             return true;
18973         }
18974         
18975         var v = this.getRawValue();
18976         
18977         if(this.multiple){
18978             v = this.getValue();
18979         }
18980         
18981         if(this.disabled || this.allowBlank || v.length){
18982             this.markValid();
18983             return true;
18984         }
18985         
18986         this.markInvalid();
18987         return false;
18988     },
18989     
18990     tickableInputEl : function()
18991     {
18992         if(!this.tickable || !this.editable){
18993             return this.inputEl();
18994         }
18995         
18996         return this.inputEl().select('.roo-select2-search-field-input', true).first();
18997     },
18998     
18999     
19000     getAutoCreateTouchView : function()
19001     {
19002         var id = Roo.id();
19003         
19004         var cfg = {
19005             cls: 'form-group' //input-group
19006         };
19007         
19008         var input =  {
19009             tag: 'input',
19010             id : id,
19011             type : this.inputType,
19012             cls : 'form-control x-combo-noedit',
19013             autocomplete: 'new-password',
19014             placeholder : this.placeholder || '',
19015             readonly : true
19016         };
19017         
19018         if (this.name) {
19019             input.name = this.name;
19020         }
19021         
19022         if (this.size) {
19023             input.cls += ' input-' + this.size;
19024         }
19025         
19026         if (this.disabled) {
19027             input.disabled = true;
19028         }
19029         
19030         var inputblock = {
19031             cls : 'roo-combobox-wrap',
19032             cn : [
19033                 input
19034             ]
19035         };
19036         
19037         if(this.before){
19038             inputblock.cls += ' input-group';
19039             
19040             inputblock.cn.unshift({
19041                 tag :'span',
19042                 cls : 'input-group-addon input-group-prepend input-group-text',
19043                 html : this.before
19044             });
19045         }
19046         
19047         if(this.removable && !this.multiple){
19048             inputblock.cls += ' roo-removable';
19049             
19050             inputblock.cn.push({
19051                 tag: 'button',
19052                 html : 'x',
19053                 cls : 'roo-combo-removable-btn close'
19054             });
19055         }
19056
19057         if(this.hasFeedback && !this.allowBlank){
19058             
19059             inputblock.cls += ' has-feedback';
19060             
19061             inputblock.cn.push({
19062                 tag: 'span',
19063                 cls: 'glyphicon form-control-feedback'
19064             });
19065             
19066         }
19067         
19068         if (this.after) {
19069             
19070             inputblock.cls += (this.before) ? '' : ' input-group';
19071             
19072             inputblock.cn.push({
19073                 tag :'span',
19074                 cls : 'input-group-addon input-group-append input-group-text',
19075                 html : this.after
19076             });
19077         }
19078
19079         
19080         var ibwrap = inputblock;
19081         
19082         if(this.multiple){
19083             ibwrap = {
19084                 tag: 'ul',
19085                 cls: 'roo-select2-choices',
19086                 cn:[
19087                     {
19088                         tag: 'li',
19089                         cls: 'roo-select2-search-field',
19090                         cn: [
19091
19092                             inputblock
19093                         ]
19094                     }
19095                 ]
19096             };
19097         
19098             
19099         }
19100         
19101         var combobox = {
19102             cls: 'roo-select2-container input-group roo-touchview-combobox ',
19103             cn: [
19104                 {
19105                     tag: 'input',
19106                     type : 'hidden',
19107                     cls: 'form-hidden-field'
19108                 },
19109                 ibwrap
19110             ]
19111         };
19112         
19113         if(!this.multiple && this.showToggleBtn){
19114             
19115             var caret = {
19116                 cls: 'caret'
19117             };
19118             
19119             if (this.caret != false) {
19120                 caret = {
19121                      tag: 'i',
19122                      cls: 'fa fa-' + this.caret
19123                 };
19124                 
19125             }
19126             
19127             combobox.cn.push({
19128                 tag :'span',
19129                 cls : 'input-group-addon input-group-append input-group-text btn dropdown-toggle',
19130                 cn : [
19131                     Roo.bootstrap.version == 3 ? caret : '',
19132                     {
19133                         tag: 'span',
19134                         cls: 'combobox-clear',
19135                         cn  : [
19136                             {
19137                                 tag : 'i',
19138                                 cls: 'icon-remove'
19139                             }
19140                         ]
19141                     }
19142                 ]
19143
19144             })
19145         }
19146         
19147         if(this.multiple){
19148             combobox.cls += ' roo-select2-container-multi';
19149         }
19150         
19151         var required =  this.allowBlank ?  {
19152                     tag : 'i',
19153                     style: 'display: none'
19154                 } : {
19155                    tag : 'i',
19156                    cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
19157                    tooltip : 'This field is required'
19158                 };
19159         
19160         var align = this.labelAlign || this.parentLabelAlign();
19161         
19162         if (align ==='left' && this.fieldLabel.length) {
19163
19164             cfg.cn = [
19165                 required,
19166                 {
19167                     tag: 'label',
19168                     cls : 'control-label col-form-label',
19169                     html : this.fieldLabel
19170
19171                 },
19172                 {
19173                     cls : 'roo-combobox-wrap ', 
19174                     cn: [
19175                         combobox
19176                     ]
19177                 }
19178             ];
19179             
19180             var labelCfg = cfg.cn[1];
19181             var contentCfg = cfg.cn[2];
19182             
19183
19184             if(this.indicatorpos == 'right'){
19185                 cfg.cn = [
19186                     {
19187                         tag: 'label',
19188                         'for' :  id,
19189                         cls : 'control-label col-form-label',
19190                         cn : [
19191                             {
19192                                 tag : 'span',
19193                                 html : this.fieldLabel
19194                             },
19195                             required
19196                         ]
19197                     },
19198                     {
19199                         cls : "roo-combobox-wrap ",
19200                         cn: [
19201                             combobox
19202                         ]
19203                     }
19204
19205                 ];
19206                 
19207                 labelCfg = cfg.cn[0];
19208                 contentCfg = cfg.cn[1];
19209             }
19210             
19211            
19212             
19213             if(this.labelWidth > 12){
19214                 labelCfg.style = "width: " + this.labelWidth + 'px';
19215             }
19216            
19217             if(this.labelWidth < 13 && this.labelmd == 0){
19218                 this.labelmd = this.labelWidth;
19219             }
19220             
19221             if(this.labellg > 0){
19222                 labelCfg.cls += ' col-lg-' + this.labellg;
19223                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
19224             }
19225             
19226             if(this.labelmd > 0){
19227                 labelCfg.cls += ' col-md-' + this.labelmd;
19228                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
19229             }
19230             
19231             if(this.labelsm > 0){
19232                 labelCfg.cls += ' col-sm-' + this.labelsm;
19233                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
19234             }
19235             
19236             if(this.labelxs > 0){
19237                 labelCfg.cls += ' col-xs-' + this.labelxs;
19238                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
19239             }
19240                 
19241                 
19242         } else if ( this.fieldLabel.length) {
19243             cfg.cn = [
19244                required,
19245                 {
19246                     tag: 'label',
19247                     cls : 'control-label',
19248                     html : this.fieldLabel
19249
19250                 },
19251                 {
19252                     cls : '', 
19253                     cn: [
19254                         combobox
19255                     ]
19256                 }
19257             ];
19258             
19259             if(this.indicatorpos == 'right'){
19260                 cfg.cn = [
19261                     {
19262                         tag: 'label',
19263                         cls : 'control-label',
19264                         html : this.fieldLabel,
19265                         cn : [
19266                             required
19267                         ]
19268                     },
19269                     {
19270                         cls : '', 
19271                         cn: [
19272                             combobox
19273                         ]
19274                     }
19275                 ];
19276             }
19277         } else {
19278             cfg.cn = combobox;    
19279         }
19280         
19281         
19282         var settings = this;
19283         
19284         ['xs','sm','md','lg'].map(function(size){
19285             if (settings[size]) {
19286                 cfg.cls += ' col-' + size + '-' + settings[size];
19287             }
19288         });
19289         
19290         return cfg;
19291     },
19292     
19293     initTouchView : function()
19294     {
19295         this.renderTouchView();
19296         
19297         this.touchViewEl.on('scroll', function(){
19298             this.el.dom.scrollTop = 0;
19299         }, this);
19300         
19301         this.originalValue = this.getValue();
19302         
19303         this.triggerEl = this.el.select('span.dropdown-toggle',true).first();
19304         
19305         this.inputEl().on("click", this.showTouchView, this);
19306         if (this.triggerEl) {
19307             this.triggerEl.on("click", this.showTouchView, this);
19308         }
19309         
19310         
19311         this.touchViewFooterEl.select('.roo-touch-view-cancel', true).first().on('click', this.hideTouchView, this);
19312         this.touchViewFooterEl.select('.roo-touch-view-ok', true).first().on('click', this.setTouchViewValue, this);
19313         
19314         this.maskEl = new Roo.LoadMask(this.touchViewEl, { store : this.store, msgCls: 'roo-el-mask-msg' });
19315         
19316         this.store.on('beforeload', this.onTouchViewBeforeLoad, this);
19317         this.store.on('load', this.onTouchViewLoad, this);
19318         this.store.on('loadexception', this.onTouchViewLoadException, this);
19319         
19320         if(this.hiddenName){
19321             
19322             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
19323             
19324             this.hiddenField.dom.value =
19325                 this.hiddenValue !== undefined ? this.hiddenValue :
19326                 this.value !== undefined ? this.value : '';
19327         
19328             this.el.dom.removeAttribute('name');
19329             this.hiddenField.dom.setAttribute('name', this.hiddenName);
19330         }
19331         
19332         if(this.multiple){
19333             this.choices = this.el.select('ul.roo-select2-choices', true).first();
19334             this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
19335         }
19336         
19337         if(this.removable && !this.multiple){
19338             var close = this.closeTriggerEl();
19339             if(close){
19340                 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
19341                 close.on('click', this.removeBtnClick, this, close);
19342             }
19343         }
19344         /*
19345          * fix the bug in Safari iOS8
19346          */
19347         this.inputEl().on("focus", function(e){
19348             document.activeElement.blur();
19349         }, this);
19350         
19351         this._touchViewMask = Roo.DomHelper.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
19352         
19353         return;
19354         
19355         
19356     },
19357     
19358     renderTouchView : function()
19359     {
19360         this.touchViewEl = Roo.get(document.body).createChild(Roo.bootstrap.form.ComboBox.touchViewTemplate);
19361         this.touchViewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
19362         
19363         this.touchViewHeaderEl = this.touchViewEl.select('.modal-header', true).first();
19364         this.touchViewHeaderEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
19365         
19366         this.touchViewBodyEl = this.touchViewEl.select('.modal-body', true).first();
19367         this.touchViewBodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
19368         this.touchViewBodyEl.setStyle('overflow', 'auto');
19369         
19370         this.touchViewListGroup = this.touchViewBodyEl.select('.list-group', true).first();
19371         this.touchViewListGroup.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
19372         
19373         this.touchViewFooterEl = this.touchViewEl.select('.modal-footer', true).first();
19374         this.touchViewFooterEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
19375         
19376     },
19377     
19378     showTouchView : function()
19379     {
19380         if(this.disabled){
19381             return;
19382         }
19383         
19384         this.touchViewHeaderEl.hide();
19385
19386         if(this.modalTitle.length){
19387             this.touchViewHeaderEl.dom.innerHTML = this.modalTitle;
19388             this.touchViewHeaderEl.show();
19389         }
19390
19391         this.touchViewEl.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
19392         this.touchViewEl.show();
19393
19394         this.touchViewEl.select('.modal-dialog', true).first().setStyle({ margin : '0px', width : '100%'});
19395         
19396         //this.touchViewEl.select('.modal-dialog > .modal-content', true).first().setSize(
19397         //        Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
19398
19399         var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
19400
19401         if(this.modalTitle.length){
19402             bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
19403         }
19404         
19405         this.touchViewBodyEl.setHeight(bodyHeight);
19406
19407         if(this.animate){
19408             var _this = this;
19409             (function(){ _this.touchViewEl.addClass(['in','show']); }).defer(50);
19410         }else{
19411             this.touchViewEl.addClass(['in','show']);
19412         }
19413         
19414         if(this._touchViewMask){
19415             Roo.get(document.body).addClass("x-body-masked");
19416             this._touchViewMask.setSize(Roo.lib.Dom.getViewWidth(true),   Roo.lib.Dom.getViewHeight(true));
19417             this._touchViewMask.setStyle('z-index', 10000);
19418             this._touchViewMask.addClass('show');
19419         }
19420         
19421         this.doTouchViewQuery();
19422         
19423     },
19424     
19425     hideTouchView : function()
19426     {
19427         this.touchViewEl.removeClass(['in','show']);
19428
19429         if(this.animate){
19430             var _this = this;
19431             (function(){ _this.touchViewEl.setStyle('display', 'none'); }).defer(150);
19432         }else{
19433             this.touchViewEl.setStyle('display', 'none');
19434         }
19435         
19436         if(this._touchViewMask){
19437             this._touchViewMask.removeClass('show');
19438             Roo.get(document.body).removeClass("x-body-masked");
19439         }
19440     },
19441     
19442     setTouchViewValue : function()
19443     {
19444         if(this.multiple){
19445             this.clearItem();
19446         
19447             var _this = this;
19448
19449             Roo.each(this.tickItems, function(o){
19450                 this.addItem(o);
19451             }, this);
19452         }
19453         
19454         this.hideTouchView();
19455     },
19456     
19457     doTouchViewQuery : function()
19458     {
19459         var qe = {
19460             query: '',
19461             forceAll: true,
19462             combo: this,
19463             cancel:false
19464         };
19465         
19466         if(this.fireEvent('beforequery', qe) ===false || qe.cancel){
19467             return false;
19468         }
19469         
19470         if(!this.alwaysQuery || this.mode == 'local'){
19471             this.onTouchViewLoad();
19472             return;
19473         }
19474         
19475         this.store.load();
19476     },
19477     
19478     onTouchViewBeforeLoad : function(combo,opts)
19479     {
19480         return;
19481     },
19482
19483     // private
19484     onTouchViewLoad : function()
19485     {
19486         if(this.store.getCount() < 1){
19487             this.onTouchViewEmptyResults();
19488             return;
19489         }
19490         
19491         this.clearTouchView();
19492         
19493         var rawValue = this.getRawValue();
19494         
19495         var template = (this.multiple) ? Roo.bootstrap.form.ComboBox.listItemCheckbox : Roo.bootstrap.form.ComboBox.listItemRadio;
19496         
19497         this.tickItems = [];
19498         
19499         this.store.data.each(function(d, rowIndex){
19500             var row = this.touchViewListGroup.createChild(template);
19501             
19502             if(typeof(d.data.cls) != 'undefined' && d.data.cls.length){
19503                 row.addClass(d.data.cls);
19504             }
19505             
19506             if(this.displayField && typeof(d.data[this.displayField]) != 'undefined'){
19507                 var cfg = {
19508                     data : d.data,
19509                     html : d.data[this.displayField]
19510                 };
19511                 
19512                 if(this.fireEvent('touchviewdisplay', this, cfg) !== false){
19513                     row.select('.roo-combobox-list-group-item-value', true).first().dom.innerHTML = cfg.html;
19514                 }
19515             }
19516             row.removeClass('selected');
19517             if(!this.multiple && this.valueField &&
19518                     typeof(d.data[this.valueField]) != 'undefined' && d.data[this.valueField] == this.getValue())
19519             {
19520                 // radio buttons..
19521                 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
19522                 row.addClass('selected');
19523             }
19524             
19525             if(this.multiple && this.valueField &&
19526                     typeof(d.data[this.valueField]) != 'undefined' && this.getValue().indexOf(d.data[this.valueField]) != -1)
19527             {
19528                 
19529                 // checkboxes...
19530                 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
19531                 this.tickItems.push(d.data);
19532             }
19533             
19534             row.on('click', this.onTouchViewClick, this, {row : row, rowIndex : rowIndex});
19535             
19536         }, this);
19537         
19538         var firstChecked = this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).first();
19539         
19540         var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
19541
19542         if(this.modalTitle.length){
19543             bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
19544         }
19545
19546         var listHeight = this.touchViewListGroup.getHeight() + this.touchViewBodyEl.getPadding('tb') * 2;
19547         
19548         if(this.mobile_restrict_height && listHeight < bodyHeight){
19549             this.touchViewBodyEl.setHeight(listHeight);
19550         }
19551         
19552         var _this = this;
19553         
19554         if(firstChecked && listHeight > bodyHeight){
19555             (function() { firstChecked.findParent('li').scrollIntoView(_this.touchViewListGroup.dom); }).defer(500);
19556         }
19557         
19558     },
19559     
19560     onTouchViewLoadException : function()
19561     {
19562         this.hideTouchView();
19563     },
19564     
19565     onTouchViewEmptyResults : function()
19566     {
19567         this.clearTouchView();
19568         
19569         this.touchViewListGroup.createChild(Roo.bootstrap.form.ComboBox.emptyResult);
19570         
19571         this.touchViewListGroup.select('.roo-combobox-touch-view-empty-result', true).first().dom.innerHTML = this.emptyResultText;
19572         
19573     },
19574     
19575     clearTouchView : function()
19576     {
19577         this.touchViewListGroup.dom.innerHTML = '';
19578     },
19579     
19580     onTouchViewClick : function(e, el, o)
19581     {
19582         e.preventDefault();
19583         
19584         var row = o.row;
19585         var rowIndex = o.rowIndex;
19586         
19587         var r = this.store.getAt(rowIndex);
19588         
19589         if(this.fireEvent('beforeselect', this, r, rowIndex) !== false){
19590             
19591             if(!this.multiple){
19592                 Roo.each(this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).elements, function(c){
19593                     c.dom.removeAttribute('checked');
19594                 }, this);
19595
19596                 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
19597
19598                 this.setFromData(r.data);
19599
19600                 var close = this.closeTriggerEl();
19601
19602                 if(close){
19603                     close.show();
19604                 }
19605
19606                 this.hideTouchView();
19607
19608                 this.fireEvent('select', this, r, rowIndex);
19609
19610                 return;
19611             }
19612
19613             if(this.valueField && typeof(r.data[this.valueField]) != 'undefined' && this.getValue().indexOf(r.data[this.valueField]) != -1){
19614                 row.select('.roo-combobox-list-group-item-box > input', true).first().dom.removeAttribute('checked');
19615                 this.tickItems.splice(this.tickItems.indexOf(r.data), 1);
19616                 return;
19617             }
19618
19619             row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
19620             this.addItem(r.data);
19621             this.tickItems.push(r.data);
19622         }
19623     },
19624     
19625     getAutoCreateNativeIOS : function()
19626     {
19627         var cfg = {
19628             cls: 'form-group' //input-group,
19629         };
19630         
19631         var combobox =  {
19632             tag: 'select',
19633             cls : 'roo-ios-select'
19634         };
19635         
19636         if (this.name) {
19637             combobox.name = this.name;
19638         }
19639         
19640         if (this.disabled) {
19641             combobox.disabled = true;
19642         }
19643         
19644         var settings = this;
19645         
19646         ['xs','sm','md','lg'].map(function(size){
19647             if (settings[size]) {
19648                 cfg.cls += ' col-' + size + '-' + settings[size];
19649             }
19650         });
19651         
19652         cfg.cn = combobox;
19653         
19654         return cfg;
19655         
19656     },
19657     
19658     initIOSView : function()
19659     {
19660         this.store.on('load', this.onIOSViewLoad, this);
19661         
19662         return;
19663     },
19664     
19665     onIOSViewLoad : function()
19666     {
19667         if(this.store.getCount() < 1){
19668             return;
19669         }
19670         
19671         this.clearIOSView();
19672         
19673         if(this.allowBlank) {
19674             
19675             var default_text = '-- SELECT --';
19676             
19677             if(this.placeholder.length){
19678                 default_text = this.placeholder;
19679             }
19680             
19681             if(this.emptyTitle.length){
19682                 default_text += ' - ' + this.emptyTitle + ' -';
19683             }
19684             
19685             var opt = this.inputEl().createChild({
19686                 tag: 'option',
19687                 value : 0,
19688                 html : default_text
19689             });
19690             
19691             var o = {};
19692             o[this.valueField] = 0;
19693             o[this.displayField] = default_text;
19694             
19695             this.ios_options.push({
19696                 data : o,
19697                 el : opt
19698             });
19699             
19700         }
19701         
19702         this.store.data.each(function(d, rowIndex){
19703             
19704             var html = '';
19705             
19706             if(this.displayField && typeof(d.data[this.displayField]) != 'undefined'){
19707                 html = d.data[this.displayField];
19708             }
19709             
19710             var value = '';
19711             
19712             if(this.valueField && typeof(d.data[this.valueField]) != 'undefined'){
19713                 value = d.data[this.valueField];
19714             }
19715             
19716             var option = {
19717                 tag: 'option',
19718                 value : value,
19719                 html : html
19720             };
19721             
19722             if(this.value == d.data[this.valueField]){
19723                 option['selected'] = true;
19724             }
19725             
19726             var opt = this.inputEl().createChild(option);
19727             
19728             this.ios_options.push({
19729                 data : d.data,
19730                 el : opt
19731             });
19732             
19733         }, this);
19734         
19735         this.inputEl().on('change', function(){
19736            this.fireEvent('select', this);
19737         }, this);
19738         
19739     },
19740     
19741     clearIOSView: function()
19742     {
19743         this.inputEl().dom.innerHTML = '';
19744         
19745         this.ios_options = [];
19746     },
19747     
19748     setIOSValue: function(v)
19749     {
19750         this.value = v;
19751         
19752         if(!this.ios_options){
19753             return;
19754         }
19755         
19756         Roo.each(this.ios_options, function(opts){
19757            
19758            opts.el.dom.removeAttribute('selected');
19759            
19760            if(opts.data[this.valueField] != v){
19761                return;
19762            }
19763            
19764            opts.el.dom.setAttribute('selected', true);
19765            
19766         }, this);
19767     }
19768
19769     /** 
19770     * @cfg {Boolean} grow 
19771     * @hide 
19772     */
19773     /** 
19774     * @cfg {Number} growMin 
19775     * @hide 
19776     */
19777     /** 
19778     * @cfg {Number} growMax 
19779     * @hide 
19780     */
19781     /**
19782      * @hide
19783      * @method autoSize
19784      */
19785 });
19786
19787 Roo.apply(Roo.bootstrap.form.ComboBox,  {
19788     
19789     header : {
19790         tag: 'div',
19791         cls: 'modal-header',
19792         cn: [
19793             {
19794                 tag: 'h4',
19795                 cls: 'modal-title'
19796             }
19797         ]
19798     },
19799     
19800     body : {
19801         tag: 'div',
19802         cls: 'modal-body',
19803         cn: [
19804             {
19805                 tag: 'ul',
19806                 cls: 'list-group'
19807             }
19808         ]
19809     },
19810     
19811     listItemRadio : {
19812         tag: 'li',
19813         cls: 'list-group-item',
19814         cn: [
19815             {
19816                 tag: 'span',
19817                 cls: 'roo-combobox-list-group-item-value'
19818             },
19819             {
19820                 tag: 'div',
19821                 cls: 'roo-combobox-list-group-item-box pull-xs-right radio-inline radio radio-info',
19822                 cn: [
19823                     {
19824                         tag: 'input',
19825                         type: 'radio'
19826                     },
19827                     {
19828                         tag: 'label'
19829                     }
19830                 ]
19831             }
19832         ]
19833     },
19834     
19835     listItemCheckbox : {
19836         tag: 'li',
19837         cls: 'list-group-item',
19838         cn: [
19839             {
19840                 tag: 'span',
19841                 cls: 'roo-combobox-list-group-item-value'
19842             },
19843             {
19844                 tag: 'div',
19845                 cls: 'roo-combobox-list-group-item-box pull-xs-right checkbox-inline checkbox checkbox-info',
19846                 cn: [
19847                     {
19848                         tag: 'input',
19849                         type: 'checkbox'
19850                     },
19851                     {
19852                         tag: 'label'
19853                     }
19854                 ]
19855             }
19856         ]
19857     },
19858     
19859     emptyResult : {
19860         tag: 'div',
19861         cls: 'alert alert-danger roo-combobox-touch-view-empty-result'
19862     },
19863     
19864     footer : {
19865         tag: 'div',
19866         cls: 'modal-footer',
19867         cn: [
19868             {
19869                 tag: 'div',
19870                 cls: 'row',
19871                 cn: [
19872                     {
19873                         tag: 'div',
19874                         cls: 'col-xs-6 text-left',
19875                         cn: {
19876                             tag: 'button',
19877                             cls: 'btn btn-danger roo-touch-view-cancel',
19878                             html: 'Cancel'
19879                         }
19880                     },
19881                     {
19882                         tag: 'div',
19883                         cls: 'col-xs-6 text-right',
19884                         cn: {
19885                             tag: 'button',
19886                             cls: 'btn btn-success roo-touch-view-ok',
19887                             html: 'OK'
19888                         }
19889                     }
19890                 ]
19891             }
19892         ]
19893         
19894     }
19895 });
19896
19897 Roo.apply(Roo.bootstrap.form.ComboBox,  {
19898     
19899     touchViewTemplate : {
19900         tag: 'div',
19901         cls: 'modal fade roo-combobox-touch-view',
19902         cn: [
19903             {
19904                 tag: 'div',
19905                 cls: 'modal-dialog',
19906                 style : 'position:fixed', // we have to fix position....
19907                 cn: [
19908                     {
19909                         tag: 'div',
19910                         cls: 'modal-content',
19911                         cn: [
19912                             Roo.bootstrap.form.ComboBox.header,
19913                             Roo.bootstrap.form.ComboBox.body,
19914                             Roo.bootstrap.form.ComboBox.footer
19915                         ]
19916                     }
19917                 ]
19918             }
19919         ]
19920     }
19921 });/*
19922  * Based on:
19923  * Ext JS Library 1.1.1
19924  * Copyright(c) 2006-2007, Ext JS, LLC.
19925  *
19926  * Originally Released Under LGPL - original licence link has changed is not relivant.
19927  *
19928  * Fork - LGPL
19929  * <script type="text/javascript">
19930  */
19931
19932 /**
19933  * @class Roo.View
19934  * @extends Roo.util.Observable
19935  * Create a "View" for an element based on a data model or UpdateManager and the supplied DomHelper template. 
19936  * This class also supports single and multi selection modes. <br>
19937  * Create a data model bound view:
19938  <pre><code>
19939  var store = new Roo.data.Store(...);
19940
19941  var view = new Roo.View({
19942     el : "my-element",
19943     tpl : '&lt;div id="{0}"&gt;{2} - {1}&lt;/div&gt;', // auto create template
19944  
19945     singleSelect: true,
19946     selectedClass: "ydataview-selected",
19947     store: store
19948  });
19949
19950  // listen for node click?
19951  view.on("click", function(vw, index, node, e){
19952  alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
19953  });
19954
19955  // load XML data
19956  dataModel.load("foobar.xml");
19957  </code></pre>
19958  For an example of creating a JSON/UpdateManager view, see {@link Roo.JsonView}.
19959  * <br><br>
19960  * <b>Note: The root of your template must be a single node. Table/row implementations may work but are not supported due to
19961  * IE"s limited insertion support with tables and Opera"s faulty event bubbling.</b>
19962  * 
19963  * Note: old style constructor is still suported (container, template, config)
19964  * 
19965  * @constructor
19966  * Create a new View
19967  * @param {Object} config The config object
19968  * 
19969  */
19970 Roo.View = function(config, depreciated_tpl, depreciated_config){
19971     
19972     this.parent = false;
19973     
19974     if (typeof(depreciated_tpl) == 'undefined') {
19975         // new way.. - universal constructor.
19976         Roo.apply(this, config);
19977         this.el  = Roo.get(this.el);
19978     } else {
19979         // old format..
19980         this.el  = Roo.get(config);
19981         this.tpl = depreciated_tpl;
19982         Roo.apply(this, depreciated_config);
19983     }
19984     this.wrapEl  = this.el.wrap().wrap();
19985     ///this.el = this.wrapEla.appendChild(document.createElement("div"));
19986     
19987     
19988     if(typeof(this.tpl) == "string"){
19989         this.tpl = new Roo.Template(this.tpl);
19990     } else {
19991         // support xtype ctors..
19992         this.tpl = new Roo.factory(this.tpl, Roo);
19993     }
19994     
19995     
19996     this.tpl.compile();
19997     
19998     /** @private */
19999     this.addEvents({
20000         /**
20001          * @event beforeclick
20002          * Fires before a click is processed. Returns false to cancel the default action.
20003          * @param {Roo.View} this
20004          * @param {Number} index The index of the target node
20005          * @param {HTMLElement} node The target node
20006          * @param {Roo.EventObject} e The raw event object
20007          */
20008             "beforeclick" : true,
20009         /**
20010          * @event click
20011          * Fires when a template node is clicked.
20012          * @param {Roo.View} this
20013          * @param {Number} index The index of the target node
20014          * @param {HTMLElement} node The target node
20015          * @param {Roo.EventObject} e The raw event object
20016          */
20017             "click" : true,
20018         /**
20019          * @event dblclick
20020          * Fires when a template node is double clicked.
20021          * @param {Roo.View} this
20022          * @param {Number} index The index of the target node
20023          * @param {HTMLElement} node The target node
20024          * @param {Roo.EventObject} e The raw event object
20025          */
20026             "dblclick" : true,
20027         /**
20028          * @event contextmenu
20029          * Fires when a template node is right clicked.
20030          * @param {Roo.View} this
20031          * @param {Number} index The index of the target node
20032          * @param {HTMLElement} node The target node
20033          * @param {Roo.EventObject} e The raw event object
20034          */
20035             "contextmenu" : true,
20036         /**
20037          * @event selectionchange
20038          * Fires when the selected nodes change.
20039          * @param {Roo.View} this
20040          * @param {Array} selections Array of the selected nodes
20041          */
20042             "selectionchange" : true,
20043     
20044         /**
20045          * @event beforeselect
20046          * Fires before a selection is made. If any handlers return false, the selection is cancelled.
20047          * @param {Roo.View} this
20048          * @param {HTMLElement} node The node to be selected
20049          * @param {Array} selections Array of currently selected nodes
20050          */
20051             "beforeselect" : true,
20052         /**
20053          * @event preparedata
20054          * Fires on every row to render, to allow you to change the data.
20055          * @param {Roo.View} this
20056          * @param {Object} data to be rendered (change this)
20057          */
20058           "preparedata" : true
20059           
20060           
20061         });
20062
20063
20064
20065     this.el.on({
20066         "click": this.onClick,
20067         "dblclick": this.onDblClick,
20068         "contextmenu": this.onContextMenu,
20069         scope:this
20070     });
20071
20072     this.selections = [];
20073     this.nodes = [];
20074     this.cmp = new Roo.CompositeElementLite([]);
20075     if(this.store){
20076         this.store = Roo.factory(this.store, Roo.data);
20077         this.setStore(this.store, true);
20078     }
20079     
20080     if ( this.footer && this.footer.xtype) {
20081            
20082          var fctr = this.wrapEl.appendChild(document.createElement("div"));
20083         
20084         this.footer.dataSource = this.store;
20085         this.footer.container = fctr;
20086         this.footer = Roo.factory(this.footer, Roo);
20087         fctr.insertFirst(this.el);
20088         
20089         // this is a bit insane - as the paging toolbar seems to detach the el..
20090 //        dom.parentNode.parentNode.parentNode
20091          // they get detached?
20092     }
20093     
20094     
20095     Roo.View.superclass.constructor.call(this);
20096     
20097     
20098 };
20099
20100 Roo.extend(Roo.View, Roo.util.Observable, {
20101     
20102      /**
20103      * @cfg {Roo.data.Store} store Data store to load data from.
20104      */
20105     store : false,
20106     
20107     /**
20108      * @cfg {String|Roo.Element} el The container element.
20109      */
20110     el : '',
20111     
20112     /**
20113      * @cfg {String|Roo.Template} tpl The template used by this View 
20114      */
20115     tpl : false,
20116     /**
20117      * @cfg {String} dataName the named area of the template to use as the data area
20118      *                          Works with domtemplates roo-name="name"
20119      */
20120     dataName: false,
20121     /**
20122      * @cfg {String} selectedClass The css class to add to selected nodes
20123      */
20124     selectedClass : "x-view-selected",
20125      /**
20126      * @cfg {String} emptyText The empty text to show when nothing is loaded.
20127      */
20128     emptyText : "",
20129     
20130     /**
20131      * @cfg {String} text to display on mask (default Loading)
20132      */
20133     mask : false,
20134     /**
20135      * @cfg {Boolean} multiSelect Allow multiple selection
20136      */
20137     multiSelect : false,
20138     /**
20139      * @cfg {Boolean} singleSelect Allow single selection
20140      */
20141     singleSelect:  false,
20142     
20143     /**
20144      * @cfg {Boolean} toggleSelect - selecting 
20145      */
20146     toggleSelect : false,
20147     
20148     /**
20149      * @cfg {Boolean} tickable - selecting 
20150      */
20151     tickable : false,
20152     
20153     /**
20154      * Returns the element this view is bound to.
20155      * @return {Roo.Element}
20156      */
20157     getEl : function(){
20158         return this.wrapEl;
20159     },
20160     
20161     
20162
20163     /**
20164      * Refreshes the view. - called by datachanged on the store. - do not call directly.
20165      */
20166     refresh : function(){
20167         //Roo.log('refresh');
20168         var t = this.tpl;
20169         
20170         // if we are using something like 'domtemplate', then
20171         // the what gets used is:
20172         // t.applySubtemplate(NAME, data, wrapping data..)
20173         // the outer template then get' applied with
20174         //     the store 'extra data'
20175         // and the body get's added to the
20176         //      roo-name="data" node?
20177         //      <span class='roo-tpl-{name}'></span> ?????
20178         
20179         
20180         
20181         this.clearSelections();
20182         this.el.update("");
20183         var html = [];
20184         var records = this.store.getRange();
20185         if(records.length < 1) {
20186             
20187             // is this valid??  = should it render a template??
20188             
20189             this.el.update(this.emptyText);
20190             return;
20191         }
20192         var el = this.el;
20193         if (this.dataName) {
20194             this.el.update(t.apply(this.store.meta)); //????
20195             el = this.el.child('.roo-tpl-' + this.dataName);
20196         }
20197         
20198         for(var i = 0, len = records.length; i < len; i++){
20199             var data = this.prepareData(records[i].data, i, records[i]);
20200             this.fireEvent("preparedata", this, data, i, records[i]);
20201             
20202             var d = Roo.apply({}, data);
20203             
20204             if(this.tickable){
20205                 Roo.apply(d, {'roo-id' : Roo.id()});
20206                 
20207                 var _this = this;
20208             
20209                 Roo.each(this.parent.item, function(item){
20210                     if(item[_this.parent.valueField] != data[_this.parent.valueField]){
20211                         return;
20212                     }
20213                     Roo.apply(d, {'roo-data-checked' : 'checked'});
20214                 });
20215             }
20216             
20217             html[html.length] = Roo.util.Format.trim(
20218                 this.dataName ?
20219                     t.applySubtemplate(this.dataName, d, this.store.meta) :
20220                     t.apply(d)
20221             );
20222         }
20223         
20224         
20225         
20226         el.update(html.join(""));
20227         this.nodes = el.dom.childNodes;
20228         this.updateIndexes(0);
20229     },
20230     
20231
20232     /**
20233      * Function to override to reformat the data that is sent to
20234      * the template for each node.
20235      * DEPRICATED - use the preparedata event handler.
20236      * @param {Array/Object} data The raw data (array of colData for a data model bound view or
20237      * a JSON object for an UpdateManager bound view).
20238      */
20239     prepareData : function(data, index, record)
20240     {
20241         this.fireEvent("preparedata", this, data, index, record);
20242         return data;
20243     },
20244
20245     onUpdate : function(ds, record){
20246         // Roo.log('on update');   
20247         this.clearSelections();
20248         var index = this.store.indexOf(record);
20249         var n = this.nodes[index];
20250         this.tpl.insertBefore(n, this.prepareData(record.data, index, record));
20251         n.parentNode.removeChild(n);
20252         this.updateIndexes(index, index);
20253     },
20254
20255     
20256     
20257 // --------- FIXME     
20258     onAdd : function(ds, records, index)
20259     {
20260         //Roo.log(['on Add', ds, records, index] );        
20261         this.clearSelections();
20262         if(this.nodes.length == 0){
20263             this.refresh();
20264             return;
20265         }
20266         var n = this.nodes[index];
20267         for(var i = 0, len = records.length; i < len; i++){
20268             var d = this.prepareData(records[i].data, i, records[i]);
20269             if(n){
20270                 this.tpl.insertBefore(n, d);
20271             }else{
20272                 
20273                 this.tpl.append(this.el, d);
20274             }
20275         }
20276         this.updateIndexes(index);
20277     },
20278
20279     onRemove : function(ds, record, index){
20280        // Roo.log('onRemove');
20281         this.clearSelections();
20282         var el = this.dataName  ?
20283             this.el.child('.roo-tpl-' + this.dataName) :
20284             this.el; 
20285         
20286         el.dom.removeChild(this.nodes[index]);
20287         this.updateIndexes(index);
20288     },
20289
20290     /**
20291      * Refresh an individual node.
20292      * @param {Number} index
20293      */
20294     refreshNode : function(index){
20295         this.onUpdate(this.store, this.store.getAt(index));
20296     },
20297
20298     updateIndexes : function(startIndex, endIndex){
20299         var ns = this.nodes;
20300         startIndex = startIndex || 0;
20301         endIndex = endIndex || ns.length - 1;
20302         for(var i = startIndex; i <= endIndex; i++){
20303             ns[i].nodeIndex = i;
20304         }
20305     },
20306
20307     /**
20308      * Changes the data store this view uses and refresh the view.
20309      * @param {Store} store
20310      */
20311     setStore : function(store, initial){
20312         if(!initial && this.store){
20313             this.store.un("datachanged", this.refresh);
20314             this.store.un("add", this.onAdd);
20315             this.store.un("remove", this.onRemove);
20316             this.store.un("update", this.onUpdate);
20317             this.store.un("clear", this.refresh);
20318             this.store.un("beforeload", this.onBeforeLoad);
20319             this.store.un("load", this.onLoad);
20320             this.store.un("loadexception", this.onLoad);
20321         }
20322         if(store){
20323           
20324             store.on("datachanged", this.refresh, this);
20325             store.on("add", this.onAdd, this);
20326             store.on("remove", this.onRemove, this);
20327             store.on("update", this.onUpdate, this);
20328             store.on("clear", this.refresh, this);
20329             store.on("beforeload", this.onBeforeLoad, this);
20330             store.on("load", this.onLoad, this);
20331             store.on("loadexception", this.onLoad, this);
20332         }
20333         
20334         if(store){
20335             this.refresh();
20336         }
20337     },
20338     /**
20339      * onbeforeLoad - masks the loading area.
20340      *
20341      */
20342     onBeforeLoad : function(store,opts)
20343     {
20344          //Roo.log('onBeforeLoad');   
20345         if (!opts.add) {
20346             this.el.update("");
20347         }
20348         this.el.mask(this.mask ? this.mask : "Loading" ); 
20349     },
20350     onLoad : function ()
20351     {
20352         this.el.unmask();
20353     },
20354     
20355
20356     /**
20357      * Returns the template node the passed child belongs to or null if it doesn't belong to one.
20358      * @param {HTMLElement} node
20359      * @return {HTMLElement} The template node
20360      */
20361     findItemFromChild : function(node){
20362         var el = this.dataName  ?
20363             this.el.child('.roo-tpl-' + this.dataName,true) :
20364             this.el.dom; 
20365         
20366         if(!node || node.parentNode == el){
20367                     return node;
20368             }
20369             var p = node.parentNode;
20370             while(p && p != el){
20371             if(p.parentNode == el){
20372                 return p;
20373             }
20374             p = p.parentNode;
20375         }
20376             return null;
20377     },
20378
20379     /** @ignore */
20380     onClick : function(e){
20381         var item = this.findItemFromChild(e.getTarget());
20382         if(item){
20383             var index = this.indexOf(item);
20384             if(this.onItemClick(item, index, e) !== false){
20385                 this.fireEvent("click", this, index, item, e);
20386             }
20387         }else{
20388             this.clearSelections();
20389         }
20390     },
20391
20392     /** @ignore */
20393     onContextMenu : function(e){
20394         var item = this.findItemFromChild(e.getTarget());
20395         if(item){
20396             this.fireEvent("contextmenu", this, this.indexOf(item), item, e);
20397         }
20398     },
20399
20400     /** @ignore */
20401     onDblClick : function(e){
20402         var item = this.findItemFromChild(e.getTarget());
20403         if(item){
20404             this.fireEvent("dblclick", this, this.indexOf(item), item, e);
20405         }
20406     },
20407
20408     onItemClick : function(item, index, e)
20409     {
20410         if(this.fireEvent("beforeclick", this, index, item, e) === false){
20411             return false;
20412         }
20413         if (this.toggleSelect) {
20414             var m = this.isSelected(item) ? 'unselect' : 'select';
20415             //Roo.log(m);
20416             var _t = this;
20417             _t[m](item, true, false);
20418             return true;
20419         }
20420         if(this.multiSelect || this.singleSelect){
20421             if(this.multiSelect && e.shiftKey && this.lastSelection){
20422                 this.select(this.getNodes(this.indexOf(this.lastSelection), index), false);
20423             }else{
20424                 this.select(item, this.multiSelect && e.ctrlKey);
20425                 this.lastSelection = item;
20426             }
20427             
20428             if(!this.tickable){
20429                 e.preventDefault();
20430             }
20431             
20432         }
20433         return true;
20434     },
20435
20436     /**
20437      * Get the number of selected nodes.
20438      * @return {Number}
20439      */
20440     getSelectionCount : function(){
20441         return this.selections.length;
20442     },
20443
20444     /**
20445      * Get the currently selected nodes.
20446      * @return {Array} An array of HTMLElements
20447      */
20448     getSelectedNodes : function(){
20449         return this.selections;
20450     },
20451
20452     /**
20453      * Get the indexes of the selected nodes.
20454      * @return {Array}
20455      */
20456     getSelectedIndexes : function(){
20457         var indexes = [], s = this.selections;
20458         for(var i = 0, len = s.length; i < len; i++){
20459             indexes.push(s[i].nodeIndex);
20460         }
20461         return indexes;
20462     },
20463
20464     /**
20465      * Clear all selections
20466      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange event
20467      */
20468     clearSelections : function(suppressEvent){
20469         if(this.nodes && (this.multiSelect || this.singleSelect) && this.selections.length > 0){
20470             this.cmp.elements = this.selections;
20471             this.cmp.removeClass(this.selectedClass);
20472             this.selections = [];
20473             if(!suppressEvent){
20474                 this.fireEvent("selectionchange", this, this.selections);
20475             }
20476         }
20477     },
20478
20479     /**
20480      * Returns true if the passed node is selected
20481      * @param {HTMLElement/Number} node The node or node index
20482      * @return {Boolean}
20483      */
20484     isSelected : function(node){
20485         var s = this.selections;
20486         if(s.length < 1){
20487             return false;
20488         }
20489         node = this.getNode(node);
20490         return s.indexOf(node) !== -1;
20491     },
20492
20493     /**
20494      * Selects nodes.
20495      * @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
20496      * @param {Boolean} keepExisting (optional) true to keep existing selections
20497      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
20498      */
20499     select : function(nodeInfo, keepExisting, suppressEvent){
20500         if(nodeInfo instanceof Array){
20501             if(!keepExisting){
20502                 this.clearSelections(true);
20503             }
20504             for(var i = 0, len = nodeInfo.length; i < len; i++){
20505                 this.select(nodeInfo[i], true, true);
20506             }
20507             return;
20508         } 
20509         var node = this.getNode(nodeInfo);
20510         if(!node || this.isSelected(node)){
20511             return; // already selected.
20512         }
20513         if(!keepExisting){
20514             this.clearSelections(true);
20515         }
20516         
20517         if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
20518             Roo.fly(node).addClass(this.selectedClass);
20519             this.selections.push(node);
20520             if(!suppressEvent){
20521                 this.fireEvent("selectionchange", this, this.selections);
20522             }
20523         }
20524         
20525         
20526     },
20527       /**
20528      * Unselects nodes.
20529      * @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
20530      * @param {Boolean} keepExisting (optional) true IGNORED (for campatibility with select)
20531      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
20532      */
20533     unselect : function(nodeInfo, keepExisting, suppressEvent)
20534     {
20535         if(nodeInfo instanceof Array){
20536             Roo.each(this.selections, function(s) {
20537                 this.unselect(s, nodeInfo);
20538             }, this);
20539             return;
20540         }
20541         var node = this.getNode(nodeInfo);
20542         if(!node || !this.isSelected(node)){
20543             //Roo.log("not selected");
20544             return; // not selected.
20545         }
20546         // fireevent???
20547         var ns = [];
20548         Roo.each(this.selections, function(s) {
20549             if (s == node ) {
20550                 Roo.fly(node).removeClass(this.selectedClass);
20551
20552                 return;
20553             }
20554             ns.push(s);
20555         },this);
20556         
20557         this.selections= ns;
20558         this.fireEvent("selectionchange", this, this.selections);
20559     },
20560
20561     /**
20562      * Gets a template node.
20563      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
20564      * @return {HTMLElement} The node or null if it wasn't found
20565      */
20566     getNode : function(nodeInfo){
20567         if(typeof nodeInfo == "string"){
20568             return document.getElementById(nodeInfo);
20569         }else if(typeof nodeInfo == "number"){
20570             return this.nodes[nodeInfo];
20571         }
20572         return nodeInfo;
20573     },
20574
20575     /**
20576      * Gets a range template nodes.
20577      * @param {Number} startIndex
20578      * @param {Number} endIndex
20579      * @return {Array} An array of nodes
20580      */
20581     getNodes : function(start, end){
20582         var ns = this.nodes;
20583         start = start || 0;
20584         end = typeof end == "undefined" ? ns.length - 1 : end;
20585         var nodes = [];
20586         if(start <= end){
20587             for(var i = start; i <= end; i++){
20588                 nodes.push(ns[i]);
20589             }
20590         } else{
20591             for(var i = start; i >= end; i--){
20592                 nodes.push(ns[i]);
20593             }
20594         }
20595         return nodes;
20596     },
20597
20598     /**
20599      * Finds the index of the passed node
20600      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
20601      * @return {Number} The index of the node or -1
20602      */
20603     indexOf : function(node){
20604         node = this.getNode(node);
20605         if(typeof node.nodeIndex == "number"){
20606             return node.nodeIndex;
20607         }
20608         var ns = this.nodes;
20609         for(var i = 0, len = ns.length; i < len; i++){
20610             if(ns[i] == node){
20611                 return i;
20612             }
20613         }
20614         return -1;
20615     }
20616 });
20617 /*
20618  * - LGPL
20619  *
20620  * based on jquery fullcalendar
20621  * 
20622  */
20623
20624 Roo.bootstrap = Roo.bootstrap || {};
20625 /**
20626  * @class Roo.bootstrap.Calendar
20627  * @extends Roo.bootstrap.Component
20628  * Bootstrap Calendar class
20629  * @cfg {Boolean} loadMask (true|false) default false
20630  * @cfg {Object} header generate the user specific header of the calendar, default false
20631
20632  * @constructor
20633  * Create a new Container
20634  * @param {Object} config The config object
20635  */
20636
20637
20638
20639 Roo.bootstrap.Calendar = function(config){
20640     Roo.bootstrap.Calendar.superclass.constructor.call(this, config);
20641      this.addEvents({
20642         /**
20643              * @event select
20644              * Fires when a date is selected
20645              * @param {DatePicker} this
20646              * @param {Date} date The selected date
20647              */
20648         'select': true,
20649         /**
20650              * @event monthchange
20651              * Fires when the displayed month changes 
20652              * @param {DatePicker} this
20653              * @param {Date} date The selected month
20654              */
20655         'monthchange': true,
20656         /**
20657              * @event evententer
20658              * Fires when mouse over an event
20659              * @param {Calendar} this
20660              * @param {event} Event
20661              */
20662         'evententer': true,
20663         /**
20664              * @event eventleave
20665              * Fires when the mouse leaves an
20666              * @param {Calendar} this
20667              * @param {event}
20668              */
20669         'eventleave': true,
20670         /**
20671              * @event eventclick
20672              * Fires when the mouse click an
20673              * @param {Calendar} this
20674              * @param {event}
20675              */
20676         'eventclick': true
20677         
20678     });
20679
20680 };
20681
20682 Roo.extend(Roo.bootstrap.Calendar, Roo.bootstrap.Component,  {
20683     
20684           /**
20685      * @cfg {Roo.data.Store} store
20686      * The data source for the calendar
20687      */
20688         store : false,
20689      /**
20690      * @cfg {Number} startDay
20691      * Day index at which the week should begin, 0-based (defaults to 0, which is Sunday)
20692      */
20693     startDay : 0,
20694     
20695     loadMask : false,
20696     
20697     header : false,
20698       
20699     getAutoCreate : function(){
20700         
20701         
20702         var fc_button = function(name, corner, style, content ) {
20703             return Roo.apply({},{
20704                 tag : 'span',
20705                 cls : 'fc-button fc-button-'+name+' fc-state-default ' + 
20706                          (corner.length ?
20707                             'fc-corner-' + corner.split(' ').join(' fc-corner-') :
20708                             ''
20709                         ),
20710                 html : '<SPAN class="fc-text-'+style+ '">'+content +'</SPAN>',
20711                 unselectable: 'on'
20712             });
20713         };
20714         
20715         var header = {};
20716         
20717         if(!this.header){
20718             header = {
20719                 tag : 'table',
20720                 cls : 'fc-header',
20721                 style : 'width:100%',
20722                 cn : [
20723                     {
20724                         tag: 'tr',
20725                         cn : [
20726                             {
20727                                 tag : 'td',
20728                                 cls : 'fc-header-left',
20729                                 cn : [
20730                                     fc_button('prev', 'left', 'arrow', '&#8249;' ),
20731                                     fc_button('next', 'right', 'arrow', '&#8250;' ),
20732                                     { tag: 'span', cls: 'fc-header-space' },
20733                                     fc_button('today', 'left right', '', 'today' )  // neds state disabled..
20734
20735
20736                                 ]
20737                             },
20738
20739                             {
20740                                 tag : 'td',
20741                                 cls : 'fc-header-center',
20742                                 cn : [
20743                                     {
20744                                         tag: 'span',
20745                                         cls: 'fc-header-title',
20746                                         cn : {
20747                                             tag: 'H2',
20748                                             html : 'month / year'
20749                                         }
20750                                     }
20751
20752                                 ]
20753                             },
20754                             {
20755                                 tag : 'td',
20756                                 cls : 'fc-header-right',
20757                                 cn : [
20758                               /*      fc_button('month', 'left', '', 'month' ),
20759                                     fc_button('week', '', '', 'week' ),
20760                                     fc_button('day', 'right', '', 'day' )
20761                                 */    
20762
20763                                 ]
20764                             }
20765
20766                         ]
20767                     }
20768                 ]
20769             };
20770         }
20771         
20772         header = this.header;
20773         
20774        
20775         var cal_heads = function() {
20776             var ret = [];
20777             // fixme - handle this.
20778             
20779             for (var i =0; i < Date.dayNames.length; i++) {
20780                 var d = Date.dayNames[i];
20781                 ret.push({
20782                     tag: 'th',
20783                     cls : 'fc-day-header fc-' + d.substring(0,3).toLowerCase() + ' fc-widget-header',
20784                     html : d.substring(0,3)
20785                 });
20786                 
20787             }
20788             ret[0].cls += ' fc-first';
20789             ret[6].cls += ' fc-last';
20790             return ret;
20791         };
20792         var cal_cell = function(n) {
20793             return  {
20794                 tag: 'td',
20795                 cls : 'fc-day fc-'+n + ' fc-widget-content', ///fc-other-month fc-past
20796                 cn : [
20797                     {
20798                         cn : [
20799                             {
20800                                 cls: 'fc-day-number',
20801                                 html: 'D'
20802                             },
20803                             {
20804                                 cls: 'fc-day-content',
20805                              
20806                                 cn : [
20807                                      {
20808                                         style: 'position: relative;' // height: 17px;
20809                                     }
20810                                 ]
20811                             }
20812                             
20813                             
20814                         ]
20815                     }
20816                 ]
20817                 
20818             }
20819         };
20820         var cal_rows = function() {
20821             
20822             var ret = [];
20823             for (var r = 0; r < 6; r++) {
20824                 var row= {
20825                     tag : 'tr',
20826                     cls : 'fc-week',
20827                     cn : []
20828                 };
20829                 
20830                 for (var i =0; i < Date.dayNames.length; i++) {
20831                     var d = Date.dayNames[i];
20832                     row.cn.push(cal_cell(d.substring(0,3).toLowerCase()));
20833
20834                 }
20835                 row.cn[0].cls+=' fc-first';
20836                 row.cn[0].cn[0].style = 'min-height:90px';
20837                 row.cn[6].cls+=' fc-last';
20838                 ret.push(row);
20839                 
20840             }
20841             ret[0].cls += ' fc-first';
20842             ret[4].cls += ' fc-prev-last';
20843             ret[5].cls += ' fc-last';
20844             return ret;
20845             
20846         };
20847         
20848         var cal_table = {
20849             tag: 'table',
20850             cls: 'fc-border-separate',
20851             style : 'width:100%',
20852             cellspacing  : 0,
20853             cn : [
20854                 { 
20855                     tag: 'thead',
20856                     cn : [
20857                         { 
20858                             tag: 'tr',
20859                             cls : 'fc-first fc-last',
20860                             cn : cal_heads()
20861                         }
20862                     ]
20863                 },
20864                 { 
20865                     tag: 'tbody',
20866                     cn : cal_rows()
20867                 }
20868                   
20869             ]
20870         };
20871          
20872          var cfg = {
20873             cls : 'fc fc-ltr',
20874             cn : [
20875                 header,
20876                 {
20877                     cls : 'fc-content',
20878                     style : "position: relative;",
20879                     cn : [
20880                         {
20881                             cls : 'fc-view fc-view-month fc-grid',
20882                             style : 'position: relative',
20883                             unselectable : 'on',
20884                             cn : [
20885                                 {
20886                                     cls : 'fc-event-container',
20887                                     style : 'position:absolute;z-index:8;top:0;left:0;'
20888                                 },
20889                                 cal_table
20890                             ]
20891                         }
20892                     ]
20893     
20894                 }
20895            ] 
20896             
20897         };
20898         
20899          
20900         
20901         return cfg;
20902     },
20903     
20904     
20905     initEvents : function()
20906     {
20907         if(!this.store){
20908             throw "can not find store for calendar";
20909         }
20910         
20911         var mark = {
20912             tag: "div",
20913             cls:"x-dlg-mask",
20914             style: "text-align:center",
20915             cn: [
20916                 {
20917                     tag: "div",
20918                     style: "background-color:white;width:50%;margin:250 auto",
20919                     cn: [
20920                         {
20921                             tag: "img",
20922                             src: Roo.rootURL + '/images/ux/lightbox/loading.gif' 
20923                         },
20924                         {
20925                             tag: "span",
20926                             html: "Loading"
20927                         }
20928                         
20929                     ]
20930                 }
20931             ]
20932         };
20933         this.maskEl = Roo.DomHelper.append(this.el.select('.fc-content', true).first(), mark, true);
20934         
20935         var size = this.el.select('.fc-content', true).first().getSize();
20936         this.maskEl.setSize(size.width, size.height);
20937         this.maskEl.enableDisplayMode("block");
20938         if(!this.loadMask){
20939             this.maskEl.hide();
20940         }
20941         
20942         this.store = Roo.factory(this.store, Roo.data);
20943         this.store.on('load', this.onLoad, this);
20944         this.store.on('beforeload', this.onBeforeLoad, this);
20945         
20946         this.resize();
20947         
20948         this.cells = this.el.select('.fc-day',true);
20949         //Roo.log(this.cells);
20950         this.textNodes = this.el.query('.fc-day-number');
20951         this.cells.addClassOnOver('fc-state-hover');
20952         
20953         this.el.select('.fc-button-prev',true).on('click', this.showPrevMonth, this);
20954         this.el.select('.fc-button-next',true).on('click', this.showNextMonth, this);
20955         this.el.select('.fc-button-today',true).on('click', this.showToday, this);
20956         this.el.select('.fc-button',true).addClassOnOver('fc-state-hover');
20957         
20958         this.on('monthchange', this.onMonthChange, this);
20959         
20960         this.update(new Date().clearTime());
20961     },
20962     
20963     resize : function() {
20964         var sz  = this.el.getSize();
20965         
20966         this.el.select('.fc-day-header',true).setWidth(sz.width / 7);
20967         this.el.select('.fc-day-content div',true).setHeight(34);
20968     },
20969     
20970     
20971     // private
20972     showPrevMonth : function(e){
20973         this.update(this.activeDate.add("mo", -1));
20974     },
20975     showToday : function(e){
20976         this.update(new Date().clearTime());
20977     },
20978     // private
20979     showNextMonth : function(e){
20980         this.update(this.activeDate.add("mo", 1));
20981     },
20982
20983     // private
20984     showPrevYear : function(){
20985         this.update(this.activeDate.add("y", -1));
20986     },
20987
20988     // private
20989     showNextYear : function(){
20990         this.update(this.activeDate.add("y", 1));
20991     },
20992
20993     
20994    // private
20995     update : function(date)
20996     {
20997         var vd = this.activeDate;
20998         this.activeDate = date;
20999 //        if(vd && this.el){
21000 //            var t = date.getTime();
21001 //            if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
21002 //                Roo.log('using add remove');
21003 //                
21004 //                this.fireEvent('monthchange', this, date);
21005 //                
21006 //                this.cells.removeClass("fc-state-highlight");
21007 //                this.cells.each(function(c){
21008 //                   if(c.dateValue == t){
21009 //                       c.addClass("fc-state-highlight");
21010 //                       setTimeout(function(){
21011 //                            try{c.dom.firstChild.focus();}catch(e){}
21012 //                       }, 50);
21013 //                       return false;
21014 //                   }
21015 //                   return true;
21016 //                });
21017 //                return;
21018 //            }
21019 //        }
21020         
21021         var days = date.getDaysInMonth();
21022         
21023         var firstOfMonth = date.getFirstDateOfMonth();
21024         var startingPos = firstOfMonth.getDay()-this.startDay;
21025         
21026         if(startingPos < this.startDay){
21027             startingPos += 7;
21028         }
21029         
21030         var pm = date.add(Date.MONTH, -1);
21031         var prevStart = pm.getDaysInMonth()-startingPos;
21032 //        
21033         this.cells = this.el.select('.fc-day',true);
21034         this.textNodes = this.el.query('.fc-day-number');
21035         this.cells.addClassOnOver('fc-state-hover');
21036         
21037         var cells = this.cells.elements;
21038         var textEls = this.textNodes;
21039         
21040         Roo.each(cells, function(cell){
21041             cell.removeClass([ 'fc-past', 'fc-other-month', 'fc-future', 'fc-state-highlight', 'fc-state-disabled']);
21042         });
21043         
21044         days += startingPos;
21045
21046         // convert everything to numbers so it's fast
21047         var day = 86400000;
21048         var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
21049         //Roo.log(d);
21050         //Roo.log(pm);
21051         //Roo.log(prevStart);
21052         
21053         var today = new Date().clearTime().getTime();
21054         var sel = date.clearTime().getTime();
21055         var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
21056         var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
21057         var ddMatch = this.disabledDatesRE;
21058         var ddText = this.disabledDatesText;
21059         var ddays = this.disabledDays ? this.disabledDays.join("") : false;
21060         var ddaysText = this.disabledDaysText;
21061         var format = this.format;
21062         
21063         var setCellClass = function(cal, cell){
21064             cell.row = 0;
21065             cell.events = [];
21066             cell.more = [];
21067             //Roo.log('set Cell Class');
21068             cell.title = "";
21069             var t = d.getTime();
21070             
21071             //Roo.log(d);
21072             
21073             cell.dateValue = t;
21074             if(t == today){
21075                 cell.className += " fc-today";
21076                 cell.className += " fc-state-highlight";
21077                 cell.title = cal.todayText;
21078             }
21079             if(t == sel){
21080                 // disable highlight in other month..
21081                 //cell.className += " fc-state-highlight";
21082                 
21083             }
21084             // disabling
21085             if(t < min) {
21086                 cell.className = " fc-state-disabled";
21087                 cell.title = cal.minText;
21088                 return;
21089             }
21090             if(t > max) {
21091                 cell.className = " fc-state-disabled";
21092                 cell.title = cal.maxText;
21093                 return;
21094             }
21095             if(ddays){
21096                 if(ddays.indexOf(d.getDay()) != -1){
21097                     cell.title = ddaysText;
21098                     cell.className = " fc-state-disabled";
21099                 }
21100             }
21101             if(ddMatch && format){
21102                 var fvalue = d.dateFormat(format);
21103                 if(ddMatch.test(fvalue)){
21104                     cell.title = ddText.replace("%0", fvalue);
21105                     cell.className = " fc-state-disabled";
21106                 }
21107             }
21108             
21109             if (!cell.initialClassName) {
21110                 cell.initialClassName = cell.dom.className;
21111             }
21112             
21113             cell.dom.className = cell.initialClassName  + ' ' +  cell.className;
21114         };
21115
21116         var i = 0;
21117         
21118         for(; i < startingPos; i++) {
21119             textEls[i].innerHTML = (++prevStart);
21120             d.setDate(d.getDate()+1);
21121             
21122             cells[i].className = "fc-past fc-other-month";
21123             setCellClass(this, cells[i]);
21124         }
21125         
21126         var intDay = 0;
21127         
21128         for(; i < days; i++){
21129             intDay = i - startingPos + 1;
21130             textEls[i].innerHTML = (intDay);
21131             d.setDate(d.getDate()+1);
21132             
21133             cells[i].className = ''; // "x-date-active";
21134             setCellClass(this, cells[i]);
21135         }
21136         var extraDays = 0;
21137         
21138         for(; i < 42; i++) {
21139             textEls[i].innerHTML = (++extraDays);
21140             d.setDate(d.getDate()+1);
21141             
21142             cells[i].className = "fc-future fc-other-month";
21143             setCellClass(this, cells[i]);
21144         }
21145         
21146         this.el.select('.fc-header-title h2',true).update(Date.monthNames[date.getMonth()] + " " + date.getFullYear());
21147         
21148         var totalRows = Math.ceil((date.getDaysInMonth() + date.getFirstDateOfMonth().getDay()) / 7);
21149         
21150         this.el.select('tr.fc-week.fc-prev-last',true).removeClass('fc-last');
21151         this.el.select('tr.fc-week.fc-next-last',true).addClass('fc-last').show();
21152         
21153         if(totalRows != 6){
21154             this.el.select('tr.fc-week.fc-last',true).removeClass('fc-last').addClass('fc-next-last').hide();
21155             this.el.select('tr.fc-week.fc-prev-last',true).addClass('fc-last');
21156         }
21157         
21158         this.fireEvent('monthchange', this, date);
21159         
21160         
21161         /*
21162         if(!this.internalRender){
21163             var main = this.el.dom.firstChild;
21164             var w = main.offsetWidth;
21165             this.el.setWidth(w + this.el.getBorderWidth("lr"));
21166             Roo.fly(main).setWidth(w);
21167             this.internalRender = true;
21168             // opera does not respect the auto grow header center column
21169             // then, after it gets a width opera refuses to recalculate
21170             // without a second pass
21171             if(Roo.isOpera && !this.secondPass){
21172                 main.rows[0].cells[1].style.width = (w - (main.rows[0].cells[0].offsetWidth+main.rows[0].cells[2].offsetWidth)) + "px";
21173                 this.secondPass = true;
21174                 this.update.defer(10, this, [date]);
21175             }
21176         }
21177         */
21178         
21179     },
21180     
21181     findCell : function(dt) {
21182         dt = dt.clearTime().getTime();
21183         var ret = false;
21184         this.cells.each(function(c){
21185             //Roo.log("check " +c.dateValue + '?=' + dt);
21186             if(c.dateValue == dt){
21187                 ret = c;
21188                 return false;
21189             }
21190             return true;
21191         });
21192         
21193         return ret;
21194     },
21195     
21196     findCells : function(ev) {
21197         var s = ev.start.clone().clearTime().getTime();
21198        // Roo.log(s);
21199         var e= ev.end.clone().clearTime().getTime();
21200        // Roo.log(e);
21201         var ret = [];
21202         this.cells.each(function(c){
21203              ////Roo.log("check " +c.dateValue + '<' + e + ' > ' + s);
21204             
21205             if(c.dateValue > e){
21206                 return ;
21207             }
21208             if(c.dateValue < s){
21209                 return ;
21210             }
21211             ret.push(c);
21212         });
21213         
21214         return ret;    
21215     },
21216     
21217 //    findBestRow: function(cells)
21218 //    {
21219 //        var ret = 0;
21220 //        
21221 //        for (var i =0 ; i < cells.length;i++) {
21222 //            ret  = Math.max(cells[i].rows || 0,ret);
21223 //        }
21224 //        return ret;
21225 //        
21226 //    },
21227     
21228     
21229     addItem : function(ev)
21230     {
21231         // look for vertical location slot in
21232         var cells = this.findCells(ev);
21233         
21234 //        ev.row = this.findBestRow(cells);
21235         
21236         // work out the location.
21237         
21238         var crow = false;
21239         var rows = [];
21240         for(var i =0; i < cells.length; i++) {
21241             
21242             cells[i].row = cells[0].row;
21243             
21244             if(i == 0){
21245                 cells[i].row = cells[i].row + 1;
21246             }
21247             
21248             if (!crow) {
21249                 crow = {
21250                     start : cells[i],
21251                     end :  cells[i]
21252                 };
21253                 continue;
21254             }
21255             if (crow.start.getY() == cells[i].getY()) {
21256                 // on same row.
21257                 crow.end = cells[i];
21258                 continue;
21259             }
21260             // different row.
21261             rows.push(crow);
21262             crow = {
21263                 start: cells[i],
21264                 end : cells[i]
21265             };
21266             
21267         }
21268         
21269         rows.push(crow);
21270         ev.els = [];
21271         ev.rows = rows;
21272         ev.cells = cells;
21273         
21274         cells[0].events.push(ev);
21275         
21276         this.calevents.push(ev);
21277     },
21278     
21279     clearEvents: function() {
21280         
21281         if(!this.calevents){
21282             return;
21283         }
21284         
21285         Roo.each(this.cells.elements, function(c){
21286             c.row = 0;
21287             c.events = [];
21288             c.more = [];
21289         });
21290         
21291         Roo.each(this.calevents, function(e) {
21292             Roo.each(e.els, function(el) {
21293                 el.un('mouseenter' ,this.onEventEnter, this);
21294                 el.un('mouseleave' ,this.onEventLeave, this);
21295                 el.remove();
21296             },this);
21297         },this);
21298         
21299         Roo.each(Roo.select('.fc-more-event', true).elements, function(e){
21300             e.remove();
21301         });
21302         
21303     },
21304     
21305     renderEvents: function()
21306     {   
21307         var _this = this;
21308         
21309         this.cells.each(function(c) {
21310             
21311             if(c.row < 5){
21312                 return;
21313             }
21314             
21315             var ev = c.events;
21316             
21317             var r = 4;
21318             if(c.row != c.events.length){
21319                 r = 4 - (4 - (c.row - c.events.length));
21320             }
21321             
21322             c.events = ev.slice(0, r);
21323             c.more = ev.slice(r);
21324             
21325             if(c.more.length && c.more.length == 1){
21326                 c.events.push(c.more.pop());
21327             }
21328             
21329             c.row = (c.row - ev.length) + c.events.length + ((c.more.length) ? 1 : 0);
21330             
21331         });
21332             
21333         this.cells.each(function(c) {
21334             
21335             c.select('.fc-day-content div',true).first().setHeight(Math.max(34, c.row * 20));
21336             
21337             
21338             for (var e = 0; e < c.events.length; e++){
21339                 var ev = c.events[e];
21340                 var rows = ev.rows;
21341                 
21342                 for(var i = 0; i < rows.length; i++) {
21343                 
21344                     // how many rows should it span..
21345
21346                     var  cfg = {
21347                         cls : 'roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable',
21348                         style : 'position: absolute', // left: 387px; width: 121px; top: 359px;
21349
21350                         unselectable : "on",
21351                         cn : [
21352                             {
21353                                 cls: 'fc-event-inner',
21354                                 cn : [
21355     //                                {
21356     //                                  tag:'span',
21357     //                                  cls: 'fc-event-time',
21358     //                                  html : cells.length > 1 ? '' : ev.time
21359     //                                },
21360                                     {
21361                                       tag:'span',
21362                                       cls: 'fc-event-title',
21363                                       html : String.format('{0}', ev.title)
21364                                     }
21365
21366
21367                                 ]
21368                             },
21369                             {
21370                                 cls: 'ui-resizable-handle ui-resizable-e',
21371                                 html : '&nbsp;&nbsp;&nbsp'
21372                             }
21373
21374                         ]
21375                     };
21376
21377                     if (i == 0) {
21378                         cfg.cls += ' fc-event-start';
21379                     }
21380                     if ((i+1) == rows.length) {
21381                         cfg.cls += ' fc-event-end';
21382                     }
21383
21384                     var ctr = _this.el.select('.fc-event-container',true).first();
21385                     var cg = ctr.createChild(cfg);
21386
21387                     var sbox = rows[i].start.select('.fc-day-content',true).first().getBox();
21388                     var ebox = rows[i].end.select('.fc-day-content',true).first().getBox();
21389
21390                     var r = (c.more.length) ? 1 : 0;
21391                     cg.setXY([sbox.x +2, sbox.y + ((c.row - c.events.length - r + e) * 20)]);    
21392                     cg.setWidth(ebox.right - sbox.x -2);
21393
21394                     cg.on('mouseenter' ,_this.onEventEnter, _this, ev);
21395                     cg.on('mouseleave' ,_this.onEventLeave, _this, ev);
21396                     cg.on('click', _this.onEventClick, _this, ev);
21397
21398                     ev.els.push(cg);
21399                     
21400                 }
21401                 
21402             }
21403             
21404             
21405             if(c.more.length){
21406                 var  cfg = {
21407                     cls : 'fc-more-event roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable fc-event-start fc-event-end',
21408                     style : 'position: absolute',
21409                     unselectable : "on",
21410                     cn : [
21411                         {
21412                             cls: 'fc-event-inner',
21413                             cn : [
21414                                 {
21415                                   tag:'span',
21416                                   cls: 'fc-event-title',
21417                                   html : 'More'
21418                                 }
21419
21420
21421                             ]
21422                         },
21423                         {
21424                             cls: 'ui-resizable-handle ui-resizable-e',
21425                             html : '&nbsp;&nbsp;&nbsp'
21426                         }
21427
21428                     ]
21429                 };
21430
21431                 var ctr = _this.el.select('.fc-event-container',true).first();
21432                 var cg = ctr.createChild(cfg);
21433
21434                 var sbox = c.select('.fc-day-content',true).first().getBox();
21435                 var ebox = c.select('.fc-day-content',true).first().getBox();
21436                 //Roo.log(cg);
21437                 cg.setXY([sbox.x +2, sbox.y +((c.row - 1) * 20)]);    
21438                 cg.setWidth(ebox.right - sbox.x -2);
21439
21440                 cg.on('click', _this.onMoreEventClick, _this, c.more);
21441                 
21442             }
21443             
21444         });
21445         
21446         
21447         
21448     },
21449     
21450     onEventEnter: function (e, el,event,d) {
21451         this.fireEvent('evententer', this, el, event);
21452     },
21453     
21454     onEventLeave: function (e, el,event,d) {
21455         this.fireEvent('eventleave', this, el, event);
21456     },
21457     
21458     onEventClick: function (e, el,event,d) {
21459         this.fireEvent('eventclick', this, el, event);
21460     },
21461     
21462     onMonthChange: function () {
21463         this.store.load();
21464     },
21465     
21466     onMoreEventClick: function(e, el, more)
21467     {
21468         var _this = this;
21469         
21470         this.calpopover.placement = 'right';
21471         this.calpopover.setTitle('More');
21472         
21473         this.calpopover.setContent('');
21474         
21475         var ctr = this.calpopover.el.select('.popover-content', true).first();
21476         
21477         Roo.each(more, function(m){
21478             var cfg = {
21479                 cls : 'fc-event-hori fc-event-draggable',
21480                 html : m.title
21481             };
21482             var cg = ctr.createChild(cfg);
21483             
21484             cg.on('click', _this.onEventClick, _this, m);
21485         });
21486         
21487         this.calpopover.show(el);
21488         
21489         
21490     },
21491     
21492     onLoad: function () 
21493     {   
21494         this.calevents = [];
21495         var cal = this;
21496         
21497         if(this.store.getCount() > 0){
21498             this.store.data.each(function(d){
21499                cal.addItem({
21500                     id : d.data.id,
21501                     start: (typeof(d.data.start_dt) === 'string') ? new Date.parseDate(d.data.start_dt, 'Y-m-d H:i:s') : d.data.start_dt,
21502                     end : (typeof(d.data.end_dt) === 'string') ? new Date.parseDate(d.data.end_dt, 'Y-m-d H:i:s') : d.data.end_dt,
21503                     time : d.data.start_time,
21504                     title : d.data.title,
21505                     description : d.data.description,
21506                     venue : d.data.venue
21507                 });
21508             });
21509         }
21510         
21511         this.renderEvents();
21512         
21513         if(this.calevents.length && this.loadMask){
21514             this.maskEl.hide();
21515         }
21516     },
21517     
21518     onBeforeLoad: function()
21519     {
21520         this.clearEvents();
21521         if(this.loadMask){
21522             this.maskEl.show();
21523         }
21524     }
21525 });
21526
21527  
21528  /*
21529  * - LGPL
21530  *
21531  * element
21532  * 
21533  */
21534
21535 /**
21536  * @class Roo.bootstrap.Popover
21537  * @extends Roo.bootstrap.Component
21538  * @parent none builder
21539  * @children Roo.bootstrap.Component
21540  * Bootstrap Popover class
21541  * @cfg {String} html contents of the popover   (or false to use children..)
21542  * @cfg {String} title of popover (or false to hide)
21543  * @cfg {String|function} (right|top|bottom|left|auto) placement how it is placed
21544  * @cfg {String} trigger click || hover (or false to trigger manually)
21545  * @cfg {Boolean} modal - popovers that are modal will mask the screen, and must be closed with another event.
21546  * @cfg {String|Boolean|Roo.Element} add click hander to trigger show over what element
21547  *      - if false and it has a 'parent' then it will be automatically added to that element
21548  *      - if string - Roo.get  will be called 
21549  * @cfg {Number} delay - delay before showing
21550  
21551  * @constructor
21552  * Create a new Popover
21553  * @param {Object} config The config object
21554  */
21555
21556 Roo.bootstrap.Popover = function(config){
21557     Roo.bootstrap.Popover.superclass.constructor.call(this, config);
21558     
21559     this.addEvents({
21560         // raw events
21561          /**
21562          * @event show
21563          * After the popover show
21564          * 
21565          * @param {Roo.bootstrap.Popover} this
21566          */
21567         "show" : true,
21568         /**
21569          * @event hide
21570          * After the popover hide
21571          * 
21572          * @param {Roo.bootstrap.Popover} this
21573          */
21574         "hide" : true
21575     });
21576 };
21577
21578 Roo.extend(Roo.bootstrap.Popover, Roo.bootstrap.Component,  {
21579     
21580     title: false,
21581     html: false,
21582     
21583     placement : 'right',
21584     trigger : 'hover', // hover
21585     modal : false,
21586     delay : 0,
21587     
21588     over: false,
21589     
21590     can_build_overlaid : false,
21591     
21592     maskEl : false, // the mask element
21593     headerEl : false,
21594     contentEl : false,
21595     alignEl : false, // when show is called with an element - this get's stored.
21596     
21597     getChildContainer : function()
21598     {
21599         return this.contentEl;
21600         
21601     },
21602     getPopoverHeader : function()
21603     {
21604         this.title = true; // flag not to hide it..
21605         this.headerEl.addClass('p-0');
21606         return this.headerEl
21607     },
21608     
21609     
21610     getAutoCreate : function(){
21611          
21612         var cfg = {
21613            cls : 'popover roo-dynamic shadow roo-popover' + (this.modal ? '-modal' : ''),
21614            style: 'display:block',
21615            cn : [
21616                 {
21617                     cls : 'arrow'
21618                 },
21619                 {
21620                     cls : 'popover-inner ',
21621                     cn : [
21622                         {
21623                             tag: 'h3',
21624                             cls: 'popover-title popover-header',
21625                             html : this.title === false ? '' : this.title
21626                         },
21627                         {
21628                             cls : 'popover-content popover-body '  + (this.cls || ''),
21629                             html : this.html || ''
21630                         }
21631                     ]
21632                     
21633                 }
21634            ]
21635         };
21636         
21637         return cfg;
21638     },
21639     /**
21640      * @param {string} the title
21641      */
21642     setTitle: function(str)
21643     {
21644         this.title = str;
21645         if (this.el) {
21646             this.headerEl.dom.innerHTML = str;
21647         }
21648         
21649     },
21650     /**
21651      * @param {string} the body content
21652      */
21653     setContent: function(str)
21654     {
21655         this.html = str;
21656         if (this.contentEl) {
21657             this.contentEl.dom.innerHTML = str;
21658         }
21659         
21660     },
21661     // as it get's added to the bottom of the page.
21662     onRender : function(ct, position)
21663     {
21664         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
21665         
21666         
21667         
21668         if(!this.el){
21669             var cfg = Roo.apply({},  this.getAutoCreate());
21670             cfg.id = Roo.id();
21671             
21672             if (this.cls) {
21673                 cfg.cls += ' ' + this.cls;
21674             }
21675             if (this.style) {
21676                 cfg.style = this.style;
21677             }
21678             //Roo.log("adding to ");
21679             this.el = Roo.get(document.body).createChild(cfg, position);
21680 //            Roo.log(this.el);
21681         }
21682         
21683         this.contentEl = this.el.select('.popover-content',true).first();
21684         this.headerEl =  this.el.select('.popover-title',true).first();
21685         
21686         var nitems = [];
21687         if(typeof(this.items) != 'undefined'){
21688             var items = this.items;
21689             delete this.items;
21690
21691             for(var i =0;i < items.length;i++) {
21692                 nitems.push(this.addxtype(Roo.apply({}, items[i])));
21693             }
21694         }
21695
21696         this.items = nitems;
21697         
21698         this.maskEl = Roo.DomHelper.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
21699         Roo.EventManager.onWindowResize(this.resizeMask, this, true);
21700         
21701         
21702         
21703         this.initEvents();
21704     },
21705     
21706     resizeMask : function()
21707     {
21708         this.maskEl.setSize(
21709             Roo.lib.Dom.getViewWidth(true),
21710             Roo.lib.Dom.getViewHeight(true)
21711         );
21712     },
21713     
21714     initEvents : function()
21715     {
21716         
21717         if (!this.modal) { 
21718             Roo.bootstrap.Popover.register(this);
21719         }
21720          
21721         this.arrowEl = this.el.select('.arrow',true).first();
21722         this.headerEl.setVisibilityMode(Roo.Element.DISPLAY); // probably not needed as it's default in BS4
21723         this.el.enableDisplayMode('block');
21724         this.el.hide();
21725  
21726         
21727         if (this.over === false && !this.parent()) {
21728             return; 
21729         }
21730         if (this.triggers === false) {
21731             return;
21732         }
21733          
21734         // support parent
21735         var on_el = (this.over == 'parent' || this.over === false) ? this.parent().el : Roo.get(this.over);
21736         var triggers = this.trigger ? this.trigger.split(' ') : [];
21737         Roo.each(triggers, function(trigger) {
21738         
21739             if (trigger == 'click') {
21740                 on_el.on('click', this.toggle, this);
21741             } else if (trigger != 'manual') {
21742                 var eventIn  = trigger == 'hover' ? 'mouseenter' : 'focusin';
21743                 var eventOut = trigger == 'hover' ? 'mouseleave' : 'focusout';
21744       
21745                 on_el.on(eventIn  ,this.enter, this);
21746                 on_el.on(eventOut, this.leave, this);
21747             }
21748         }, this);
21749     },
21750     
21751     
21752     // private
21753     timeout : null,
21754     hoverState : null,
21755     
21756     toggle : function () {
21757         this.hoverState == 'in' ? this.leave() : this.enter();
21758     },
21759     
21760     enter : function () {
21761         
21762         clearTimeout(this.timeout);
21763     
21764         this.hoverState = 'in';
21765     
21766         if (!this.delay || !this.delay.show) {
21767             this.show();
21768             return;
21769         }
21770         var _t = this;
21771         this.timeout = setTimeout(function () {
21772             if (_t.hoverState == 'in') {
21773                 _t.show();
21774             }
21775         }, this.delay.show)
21776     },
21777     
21778     leave : function() {
21779         clearTimeout(this.timeout);
21780     
21781         this.hoverState = 'out';
21782     
21783         if (!this.delay || !this.delay.hide) {
21784             this.hide();
21785             return;
21786         }
21787         var _t = this;
21788         this.timeout = setTimeout(function () {
21789             if (_t.hoverState == 'out') {
21790                 _t.hide();
21791             }
21792         }, this.delay.hide)
21793     },
21794     
21795     /**
21796      * update the position of the dialog
21797      * normally this is needed if the popover get's bigger - due to a Table reload etc..
21798      * 
21799      *
21800      */
21801     
21802     doAlign : function()
21803     {
21804         
21805         if (this.alignEl) {
21806             this.updatePosition(this.placement, true);
21807              
21808         } else {
21809             // this is usually just done by the builder = to show the popoup in the middle of the scren.
21810             var es = this.el.getSize();
21811             var x = Roo.lib.Dom.getViewWidth()/2;
21812             var y = Roo.lib.Dom.getViewHeight()/2;
21813             this.el.setXY([ x-(es.width/2),  y-(es.height/2)] );
21814             
21815         }
21816
21817          
21818          
21819         
21820         
21821     },
21822     
21823     /**
21824      * Show the popover
21825      * @param {Roo.Element|string|Boolean} - element to align and point to. (set align to [ pos, offset ])
21826      * @param {string} (left|right|top|bottom) position
21827      */
21828     show : function (on_el, placement)
21829     {
21830         this.placement = typeof(placement) == 'undefined' ?  this.placement   : placement;
21831         on_el = on_el || false; // default to false
21832          
21833         if (!on_el) {
21834             if (this.parent() && (this.over == 'parent' || (this.over === false))) {
21835                 on_el = this.parent().el;
21836             } else if (this.over) {
21837                 on_el = Roo.get(this.over);
21838             }
21839             
21840         }
21841         
21842         this.alignEl = Roo.get( on_el );
21843
21844         if (!this.el) {
21845             this.render(document.body);
21846         }
21847         
21848         
21849          
21850         
21851         if (this.title === false) {
21852             this.headerEl.hide();
21853         }
21854         
21855        
21856         this.el.show();
21857         this.el.dom.style.display = 'block';
21858          
21859         this.doAlign();
21860         
21861         //var arrow = this.el.select('.arrow',true).first();
21862         //arrow.set(align[2], 
21863         
21864         this.el.addClass('in');
21865         
21866          
21867         
21868         this.hoverState = 'in';
21869         
21870         if (this.modal) {
21871             this.maskEl.setSize(Roo.lib.Dom.getViewWidth(true),   Roo.lib.Dom.getViewHeight(true));
21872             this.maskEl.setStyle('z-index', Roo.bootstrap.Popover.zIndex++);
21873             this.maskEl.dom.style.display = 'block';
21874             this.maskEl.addClass('show');
21875         }
21876         this.el.setStyle('z-index', Roo.bootstrap.Popover.zIndex++);
21877  
21878         this.fireEvent('show', this);
21879         
21880     },
21881     /**
21882      * fire this manually after loading a grid in the table for example
21883      * @param {string} (left|right|top|bottom) where to try and put it (use false to use the last one)
21884      * @param {Boolean} try and move it if we cant get right position.
21885      */
21886     updatePosition : function(placement, try_move)
21887     {
21888         // allow for calling with no parameters
21889         placement = placement   ? placement :  this.placement;
21890         try_move = typeof(try_move) == 'undefined' ? true : try_move;
21891         
21892         this.el.removeClass([
21893             'fade','top','bottom', 'left', 'right','in',
21894             'bs-popover-top','bs-popover-bottom', 'bs-popover-left', 'bs-popover-right'
21895         ]);
21896         this.el.addClass(placement + ' bs-popover-' + placement);
21897         
21898         if (!this.alignEl ) {
21899             return false;
21900         }
21901         
21902         switch (placement) {
21903             case 'right':
21904                 var exact = this.el.getAlignToXY(this.alignEl, 'tl-tr', [10,0]);
21905                 var offset = this.el.getAlignToXY(this.alignEl, 'tl-tr?',[10,0]);
21906                 if (!try_move || exact.equals(offset) || exact[0] == offset[0] ) {
21907                     //normal display... or moved up/down.
21908                     this.el.setXY(offset);
21909                     var xy = this.alignEl.getAnchorXY('tr', false);
21910                     xy[0]+=2;xy[1]+=5;
21911                     this.arrowEl.setXY(xy);
21912                     return true;
21913                 }
21914                 // continue through...
21915                 return this.updatePosition('left', false);
21916                 
21917             
21918             case 'left':
21919                 var exact = this.el.getAlignToXY(this.alignEl, 'tr-tl', [-10,0]);
21920                 var offset = this.el.getAlignToXY(this.alignEl, 'tr-tl?',[-10,0]);
21921                 if (!try_move || exact.equals(offset) || exact[0] == offset[0] ) {
21922                     //normal display... or moved up/down.
21923                     this.el.setXY(offset);
21924                     var xy = this.alignEl.getAnchorXY('tl', false);
21925                     xy[0]-=10;xy[1]+=5; // << fix me
21926                     this.arrowEl.setXY(xy);
21927                     return true;
21928                 }
21929                 // call self...
21930                 return this.updatePosition('right', false);
21931             
21932             case 'top':
21933                 var exact = this.el.getAlignToXY(this.alignEl, 'b-t', [0,-10]);
21934                 var offset = this.el.getAlignToXY(this.alignEl, 'b-t?',[0,-10]);
21935                 if (!try_move || exact.equals(offset) || exact[1] == offset[1] ) {
21936                     //normal display... or moved up/down.
21937                     this.el.setXY(offset);
21938                     var xy = this.alignEl.getAnchorXY('t', false);
21939                     xy[1]-=10; // << fix me
21940                     this.arrowEl.setXY(xy);
21941                     return true;
21942                 }
21943                 // fall through
21944                return this.updatePosition('bottom', false);
21945             
21946             case 'bottom':
21947                  var exact = this.el.getAlignToXY(this.alignEl, 't-b', [0,10]);
21948                 var offset = this.el.getAlignToXY(this.alignEl, 't-b?',[0,10]);
21949                 if (!try_move || exact.equals(offset) || exact[1] == offset[1] ) {
21950                     //normal display... or moved up/down.
21951                     this.el.setXY(offset);
21952                     var xy = this.alignEl.getAnchorXY('b', false);
21953                      xy[1]+=2; // << fix me
21954                     this.arrowEl.setXY(xy);
21955                     return true;
21956                 }
21957                 // fall through
21958                 return this.updatePosition('top', false);
21959                 
21960             
21961         }
21962         
21963         
21964         return false;
21965     },
21966     
21967     hide : function()
21968     {
21969         this.el.setXY([0,0]);
21970         this.el.removeClass('in');
21971         this.el.hide();
21972         this.hoverState = null;
21973         this.maskEl.hide(); // always..
21974         this.fireEvent('hide', this);
21975     }
21976     
21977 });
21978
21979
21980 Roo.apply(Roo.bootstrap.Popover, {
21981
21982     alignment : {
21983         'left' : ['r-l', [-10,0], 'left bs-popover-left'],
21984         'right' : ['l-br', [10,0], 'right bs-popover-right'],
21985         'bottom' : ['t-b', [0,10], 'top bs-popover-top'],
21986         'top' : [ 'b-t', [0,-10], 'bottom bs-popover-bottom']
21987     },
21988     
21989     zIndex : 20001,
21990
21991     clickHander : false,
21992     
21993     
21994
21995     onMouseDown : function(e)
21996     {
21997         if (this.popups.length &&  !e.getTarget(".roo-popover")) {
21998             /// what is nothing is showing..
21999             this.hideAll();
22000         }
22001          
22002     },
22003     
22004     
22005     popups : [],
22006     
22007     register : function(popup)
22008     {
22009         if (!Roo.bootstrap.Popover.clickHandler) {
22010             Roo.bootstrap.Popover.clickHandler = Roo.get(document).on("mousedown", Roo.bootstrap.Popover.onMouseDown, Roo.bootstrap.Popover);
22011         }
22012         // hide other popups.
22013         popup.on('show', Roo.bootstrap.Popover.onShow,  popup);
22014         popup.on('hide', Roo.bootstrap.Popover.onHide,  popup);
22015         this.hideAll(); //<< why?
22016         //this.popups.push(popup);
22017     },
22018     hideAll : function()
22019     {
22020         this.popups.forEach(function(p) {
22021             p.hide();
22022         });
22023     },
22024     onShow : function() {
22025         Roo.bootstrap.Popover.popups.push(this);
22026     },
22027     onHide : function() {
22028         Roo.bootstrap.Popover.popups.remove(this);
22029     } 
22030
22031 });
22032 /**
22033  * @class Roo.bootstrap.PopoverNav
22034  * @extends Roo.bootstrap.nav.Simplebar
22035  * @parent Roo.bootstrap.Popover
22036  * @children Roo.bootstrap.nav.Group Roo.bootstrap.Container
22037  * @licence LGPL
22038  * Bootstrap Popover header navigation class
22039  * FIXME? should this go under nav?
22040  *
22041  * 
22042  * @constructor
22043  * Create a new Popover Header Navigation 
22044  * @param {Object} config The config object
22045  */
22046
22047 Roo.bootstrap.PopoverNav = function(config){
22048     Roo.bootstrap.PopoverNav.superclass.constructor.call(this, config);
22049 };
22050
22051 Roo.extend(Roo.bootstrap.PopoverNav, Roo.bootstrap.nav.Simplebar,  {
22052     
22053     
22054     container_method : 'getPopoverHeader' 
22055     
22056      
22057     
22058     
22059    
22060 });
22061
22062  
22063
22064  /*
22065  * - LGPL
22066  *
22067  * Progress
22068  * 
22069  */
22070
22071 /**
22072  * @class Roo.bootstrap.Progress
22073  * @extends Roo.bootstrap.Component
22074  * @children Roo.bootstrap.ProgressBar
22075  * Bootstrap Progress class
22076  * @cfg {Boolean} striped striped of the progress bar
22077  * @cfg {Boolean} active animated of the progress bar
22078  * 
22079  * 
22080  * @constructor
22081  * Create a new Progress
22082  * @param {Object} config The config object
22083  */
22084
22085 Roo.bootstrap.Progress = function(config){
22086     Roo.bootstrap.Progress.superclass.constructor.call(this, config);
22087 };
22088
22089 Roo.extend(Roo.bootstrap.Progress, Roo.bootstrap.Component,  {
22090     
22091     striped : false,
22092     active: false,
22093     
22094     getAutoCreate : function(){
22095         var cfg = {
22096             tag: 'div',
22097             cls: 'progress'
22098         };
22099         
22100         
22101         if(this.striped){
22102             cfg.cls += ' progress-striped';
22103         }
22104       
22105         if(this.active){
22106             cfg.cls += ' active';
22107         }
22108         
22109         
22110         return cfg;
22111     }
22112    
22113 });
22114
22115  
22116
22117  /*
22118  * - LGPL
22119  *
22120  * ProgressBar
22121  * 
22122  */
22123
22124 /**
22125  * @class Roo.bootstrap.ProgressBar
22126  * @extends Roo.bootstrap.Component
22127  * Bootstrap ProgressBar class
22128  * @cfg {Number} aria_valuenow aria-value now
22129  * @cfg {Number} aria_valuemin aria-value min
22130  * @cfg {Number} aria_valuemax aria-value max
22131  * @cfg {String} label label for the progress bar
22132  * @cfg {String} panel (success | info | warning | danger )
22133  * @cfg {String} role role of the progress bar
22134  * @cfg {String} sr_only text
22135  * 
22136  * 
22137  * @constructor
22138  * Create a new ProgressBar
22139  * @param {Object} config The config object
22140  */
22141
22142 Roo.bootstrap.ProgressBar = function(config){
22143     Roo.bootstrap.ProgressBar.superclass.constructor.call(this, config);
22144 };
22145
22146 Roo.extend(Roo.bootstrap.ProgressBar, Roo.bootstrap.Component,  {
22147     
22148     aria_valuenow : 0,
22149     aria_valuemin : 0,
22150     aria_valuemax : 100,
22151     label : false,
22152     panel : false,
22153     role : false,
22154     sr_only: false,
22155     
22156     getAutoCreate : function()
22157     {
22158         
22159         var cfg = {
22160             tag: 'div',
22161             cls: 'progress-bar',
22162             style: 'width:' + Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%'
22163         };
22164         
22165         if(this.sr_only){
22166             cfg.cn = {
22167                 tag: 'span',
22168                 cls: 'sr-only',
22169                 html: this.sr_only
22170             }
22171         }
22172         
22173         if(this.role){
22174             cfg.role = this.role;
22175         }
22176         
22177         if(this.aria_valuenow){
22178             cfg['aria-valuenow'] = this.aria_valuenow;
22179         }
22180         
22181         if(this.aria_valuemin){
22182             cfg['aria-valuemin'] = this.aria_valuemin;
22183         }
22184         
22185         if(this.aria_valuemax){
22186             cfg['aria-valuemax'] = this.aria_valuemax;
22187         }
22188         
22189         if(this.label && !this.sr_only){
22190             cfg.html = this.label;
22191         }
22192         
22193         if(this.panel){
22194             cfg.cls += ' progress-bar-' + this.panel;
22195         }
22196         
22197         return cfg;
22198     },
22199     
22200     update : function(aria_valuenow)
22201     {
22202         this.aria_valuenow = aria_valuenow;
22203         
22204         this.el.setStyle('width', Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%');
22205     }
22206    
22207 });
22208
22209  
22210
22211  /**
22212  * @class Roo.bootstrap.TabGroup
22213  * @extends Roo.bootstrap.Column
22214  * @children Roo.bootstrap.TabPanel
22215  * Bootstrap Column class
22216  * @cfg {String} navId the navigation id (for use with navbars) - will be auto generated if it does not exist..
22217  * @cfg {Boolean} carousel true to make the group behave like a carousel
22218  * @cfg {Boolean} bullets show bullets for the panels
22219  * @cfg {Boolean} autoslide (true|false) auto slide .. default false
22220  * @cfg {Number} timer auto slide timer .. default 0 millisecond
22221  * @cfg {Boolean} showarrow (true|false) show arrow default true
22222  * 
22223  * @constructor
22224  * Create a new TabGroup
22225  * @param {Object} config The config object
22226  */
22227
22228 Roo.bootstrap.TabGroup = function(config){
22229     Roo.bootstrap.TabGroup.superclass.constructor.call(this, config);
22230     if (!this.navId) {
22231         this.navId = Roo.id();
22232     }
22233     this.tabs = [];
22234     Roo.bootstrap.TabGroup.register(this);
22235     
22236 };
22237
22238 Roo.extend(Roo.bootstrap.TabGroup, Roo.bootstrap.Column,  {
22239     
22240     carousel : false,
22241     transition : false,
22242     bullets : 0,
22243     timer : 0,
22244     autoslide : false,
22245     slideFn : false,
22246     slideOnTouch : false,
22247     showarrow : true,
22248     
22249     getAutoCreate : function()
22250     {
22251         var cfg = Roo.apply({}, Roo.bootstrap.TabGroup.superclass.getAutoCreate.call(this));
22252         
22253         cfg.cls += ' tab-content';
22254         
22255         if (this.carousel) {
22256             cfg.cls += ' carousel slide';
22257             
22258             cfg.cn = [{
22259                cls : 'carousel-inner',
22260                cn : []
22261             }];
22262         
22263             if(this.bullets  && !Roo.isTouch){
22264                 
22265                 var bullets = {
22266                     cls : 'carousel-bullets',
22267                     cn : []
22268                 };
22269                
22270                 if(this.bullets_cls){
22271                     bullets.cls = bullets.cls + ' ' + this.bullets_cls;
22272                 }
22273                 
22274                 bullets.cn.push({
22275                     cls : 'clear'
22276                 });
22277                 
22278                 cfg.cn[0].cn.push(bullets);
22279             }
22280             
22281             if(this.showarrow){
22282                 cfg.cn[0].cn.push({
22283                     tag : 'div',
22284                     class : 'carousel-arrow',
22285                     cn : [
22286                         {
22287                             tag : 'div',
22288                             class : 'carousel-prev',
22289                             cn : [
22290                                 {
22291                                     tag : 'i',
22292                                     class : 'fa fa-chevron-left'
22293                                 }
22294                             ]
22295                         },
22296                         {
22297                             tag : 'div',
22298                             class : 'carousel-next',
22299                             cn : [
22300                                 {
22301                                     tag : 'i',
22302                                     class : 'fa fa-chevron-right'
22303                                 }
22304                             ]
22305                         }
22306                     ]
22307                 });
22308             }
22309             
22310         }
22311         
22312         return cfg;
22313     },
22314     
22315     initEvents:  function()
22316     {
22317 //        if(Roo.isTouch && this.slideOnTouch && !this.showarrow){
22318 //            this.el.on("touchstart", this.onTouchStart, this);
22319 //        }
22320         
22321         if(this.autoslide){
22322             var _this = this;
22323             
22324             this.slideFn = window.setInterval(function() {
22325                 _this.showPanelNext();
22326             }, this.timer);
22327         }
22328         
22329         if(this.showarrow){
22330             this.el.select('.carousel-prev', true).first().on('click', this.showPanelPrev, this);
22331             this.el.select('.carousel-next', true).first().on('click', this.showPanelNext, this);
22332         }
22333         
22334         
22335     },
22336     
22337 //    onTouchStart : function(e, el, o)
22338 //    {
22339 //        if(!this.slideOnTouch || !Roo.isTouch || Roo.get(e.getTarget()).hasClass('roo-button-text')){
22340 //            return;
22341 //        }
22342 //        
22343 //        this.showPanelNext();
22344 //    },
22345     
22346     
22347     getChildContainer : function()
22348     {
22349         return this.carousel ? this.el.select('.carousel-inner', true).first() : this.el;
22350     },
22351     
22352     /**
22353     * register a Navigation item
22354     * @param {Roo.bootstrap.nav.Item} the navitem to add
22355     */
22356     register : function(item)
22357     {
22358         this.tabs.push( item);
22359         item.navId = this.navId; // not really needed..
22360         this.addBullet();
22361     
22362     },
22363     
22364     getActivePanel : function()
22365     {
22366         var r = false;
22367         Roo.each(this.tabs, function(t) {
22368             if (t.active) {
22369                 r = t;
22370                 return false;
22371             }
22372             return null;
22373         });
22374         return r;
22375         
22376     },
22377     getPanelByName : function(n)
22378     {
22379         var r = false;
22380         Roo.each(this.tabs, function(t) {
22381             if (t.tabId == n) {
22382                 r = t;
22383                 return false;
22384             }
22385             return null;
22386         });
22387         return r;
22388     },
22389     indexOfPanel : function(p)
22390     {
22391         var r = false;
22392         Roo.each(this.tabs, function(t,i) {
22393             if (t.tabId == p.tabId) {
22394                 r = i;
22395                 return false;
22396             }
22397             return null;
22398         });
22399         return r;
22400     },
22401     /**
22402      * show a specific panel
22403      * @param {Roo.bootstrap.TabPanel|number|string} panel to change to (use the tabId to specify a specific one)
22404      * @return {boolean} false if panel was not shown (invalid entry or beforedeactivate fails.)
22405      */
22406     showPanel : function (pan)
22407     {
22408         if(this.transition || typeof(pan) == 'undefined'){
22409             Roo.log("waiting for the transitionend");
22410             return false;
22411         }
22412         
22413         if (typeof(pan) == 'number') {
22414             pan = this.tabs[pan];
22415         }
22416         
22417         if (typeof(pan) == 'string') {
22418             pan = this.getPanelByName(pan);
22419         }
22420         
22421         var cur = this.getActivePanel();
22422         
22423         if(!pan || !cur){
22424             Roo.log('pan or acitve pan is undefined');
22425             return false;
22426         }
22427         
22428         if (pan.tabId == this.getActivePanel().tabId) {
22429             return true;
22430         }
22431         
22432         if (false === cur.fireEvent('beforedeactivate')) {
22433             return false;
22434         }
22435         
22436         if(this.bullets > 0 && !Roo.isTouch){
22437             this.setActiveBullet(this.indexOfPanel(pan));
22438         }
22439         
22440         if (this.carousel && typeof(Roo.get(document.body).dom.style.transition) != 'undefined') {
22441             
22442             //class="carousel-item carousel-item-next carousel-item-left"
22443             
22444             this.transition = true;
22445             var dir = this.indexOfPanel(pan) > this.indexOfPanel(cur)  ? 'next' : 'prev';
22446             var lr = dir == 'next' ? 'left' : 'right';
22447             pan.el.addClass(dir); // or prev
22448             pan.el.addClass('carousel-item-' + dir); // or prev
22449             pan.el.dom.offsetWidth; // find the offset with - causing a reflow?
22450             cur.el.addClass(lr); // or right
22451             pan.el.addClass(lr);
22452             cur.el.addClass('carousel-item-' +lr); // or right
22453             pan.el.addClass('carousel-item-' +lr);
22454             
22455             
22456             var _this = this;
22457             cur.el.on('transitionend', function() {
22458                 Roo.log("trans end?");
22459                 
22460                 pan.el.removeClass([lr,dir, 'carousel-item-' + lr, 'carousel-item-' + dir]);
22461                 pan.setActive(true);
22462                 
22463                 cur.el.removeClass([lr, 'carousel-item-' + lr]);
22464                 cur.setActive(false);
22465                 
22466                 _this.transition = false;
22467                 
22468             }, this, { single:  true } );
22469             
22470             return true;
22471         }
22472         
22473         cur.setActive(false);
22474         pan.setActive(true);
22475         
22476         return true;
22477         
22478     },
22479     showPanelNext : function()
22480     {
22481         var i = this.indexOfPanel(this.getActivePanel());
22482         
22483         if (i >= this.tabs.length - 1 && !this.autoslide) {
22484             return;
22485         }
22486         
22487         if (i >= this.tabs.length - 1 && this.autoslide) {
22488             i = -1;
22489         }
22490         
22491         this.showPanel(this.tabs[i+1]);
22492     },
22493     
22494     showPanelPrev : function()
22495     {
22496         var i = this.indexOfPanel(this.getActivePanel());
22497         
22498         if (i  < 1 && !this.autoslide) {
22499             return;
22500         }
22501         
22502         if (i < 1 && this.autoslide) {
22503             i = this.tabs.length;
22504         }
22505         
22506         this.showPanel(this.tabs[i-1]);
22507     },
22508     
22509     
22510     addBullet: function()
22511     {
22512         if(!this.bullets || Roo.isTouch){
22513             return;
22514         }
22515         var ctr = this.el.select('.carousel-bullets',true).first();
22516         var i = this.el.select('.carousel-bullets .bullet',true).getCount() ;
22517         var bullet = ctr.createChild({
22518             cls : 'bullet bullet-' + i
22519         },ctr.dom.lastChild);
22520         
22521         
22522         var _this = this;
22523         
22524         bullet.on('click', (function(e, el, o, ii, t){
22525
22526             e.preventDefault();
22527
22528             this.showPanel(ii);
22529
22530             if(this.autoslide && this.slideFn){
22531                 clearInterval(this.slideFn);
22532                 this.slideFn = window.setInterval(function() {
22533                     _this.showPanelNext();
22534                 }, this.timer);
22535             }
22536
22537         }).createDelegate(this, [i, bullet], true));
22538                 
22539         
22540     },
22541      
22542     setActiveBullet : function(i)
22543     {
22544         if(Roo.isTouch){
22545             return;
22546         }
22547         
22548         Roo.each(this.el.select('.bullet', true).elements, function(el){
22549             el.removeClass('selected');
22550         });
22551
22552         var bullet = this.el.select('.bullet-' + i, true).first();
22553         
22554         if(!bullet){
22555             return;
22556         }
22557         
22558         bullet.addClass('selected');
22559     }
22560     
22561     
22562   
22563 });
22564
22565  
22566
22567  
22568  
22569 Roo.apply(Roo.bootstrap.TabGroup, {
22570     
22571     groups: {},
22572      /**
22573     * register a Navigation Group
22574     * @param {Roo.bootstrap.nav.Group} the navgroup to add
22575     */
22576     register : function(navgrp)
22577     {
22578         this.groups[navgrp.navId] = navgrp;
22579         
22580     },
22581     /**
22582     * fetch a Navigation Group based on the navigation ID
22583     * if one does not exist , it will get created.
22584     * @param {string} the navgroup to add
22585     * @returns {Roo.bootstrap.nav.Group} the navgroup 
22586     */
22587     get: function(navId) {
22588         if (typeof(this.groups[navId]) == 'undefined') {
22589             this.register(new Roo.bootstrap.TabGroup({ navId : navId }));
22590         }
22591         return this.groups[navId] ;
22592     }
22593     
22594     
22595     
22596 });
22597
22598  /*
22599  * - LGPL
22600  *
22601  * TabPanel
22602  * 
22603  */
22604
22605 /**
22606  * @class Roo.bootstrap.TabPanel
22607  * @extends Roo.bootstrap.Component
22608  * @children Roo.bootstrap.Component
22609  * Bootstrap TabPanel class
22610  * @cfg {Boolean} active panel active
22611  * @cfg {String} html panel content
22612  * @cfg {String} tabId  unique tab ID (will be autogenerated if not set. - used to match TabItem to Panel)
22613  * @cfg {String} navId The Roo.bootstrap.nav.Group which triggers show hide ()
22614  * @cfg {String} href click to link..
22615  * @cfg {Boolean} touchSlide if swiping slides tab to next panel (default off)
22616  * 
22617  * 
22618  * @constructor
22619  * Create a new TabPanel
22620  * @param {Object} config The config object
22621  */
22622
22623 Roo.bootstrap.TabPanel = function(config){
22624     Roo.bootstrap.TabPanel.superclass.constructor.call(this, config);
22625     this.addEvents({
22626         /**
22627              * @event changed
22628              * Fires when the active status changes
22629              * @param {Roo.bootstrap.TabPanel} this
22630              * @param {Boolean} state the new state
22631             
22632          */
22633         'changed': true,
22634         /**
22635              * @event beforedeactivate
22636              * Fires before a tab is de-activated - can be used to do validation on a form.
22637              * @param {Roo.bootstrap.TabPanel} this
22638              * @return {Boolean} false if there is an error
22639             
22640          */
22641         'beforedeactivate': true
22642      });
22643     
22644     this.tabId = this.tabId || Roo.id();
22645   
22646 };
22647
22648 Roo.extend(Roo.bootstrap.TabPanel, Roo.bootstrap.Component,  {
22649     
22650     active: false,
22651     html: false,
22652     tabId: false,
22653     navId : false,
22654     href : '',
22655     touchSlide : false,
22656     getAutoCreate : function(){
22657         
22658         
22659         var cfg = {
22660             tag: 'div',
22661             // item is needed for carousel - not sure if it has any effect otherwise
22662             cls: 'carousel-item tab-pane item' + ((this.href.length) ? ' clickable ' : ''),
22663             html: this.html || ''
22664         };
22665         
22666         if(this.active){
22667             cfg.cls += ' active';
22668         }
22669         
22670         if(this.tabId){
22671             cfg.tabId = this.tabId;
22672         }
22673         
22674         
22675         
22676         return cfg;
22677     },
22678     
22679     initEvents:  function()
22680     {
22681         var p = this.parent();
22682         
22683         this.navId = this.navId || p.navId;
22684         
22685         if (typeof(this.navId) != 'undefined') {
22686             // not really needed.. but just in case.. parent should be a NavGroup.
22687             var tg = Roo.bootstrap.TabGroup.get(this.navId);
22688             
22689             tg.register(this);
22690             
22691             var i = tg.tabs.length - 1;
22692             
22693             if(this.active && tg.bullets > 0 && i < tg.bullets){
22694                 tg.setActiveBullet(i);
22695             }
22696         }
22697         
22698         this.el.on('click', this.onClick, this);
22699         
22700         if(Roo.isTouch && this.touchSlide){
22701             this.el.on("touchstart", this.onTouchStart, this);
22702             this.el.on("touchmove", this.onTouchMove, this);
22703             this.el.on("touchend", this.onTouchEnd, this);
22704         }
22705         
22706     },
22707     
22708     onRender : function(ct, position)
22709     {
22710         Roo.bootstrap.TabPanel.superclass.onRender.call(this, ct, position);
22711     },
22712     
22713     setActive : function(state)
22714     {
22715         Roo.log("panel - set active " + this.tabId + "=" + state);
22716         
22717         this.active = state;
22718         if (!state) {
22719             this.el.removeClass('active');
22720             
22721         } else  if (!this.el.hasClass('active')) {
22722             this.el.addClass('active');
22723         }
22724         
22725         this.fireEvent('changed', this, state);
22726     },
22727     
22728     onClick : function(e)
22729     {
22730         e.preventDefault();
22731         
22732         if(!this.href.length){
22733             return;
22734         }
22735         
22736         window.location.href = this.href;
22737     },
22738     
22739     startX : 0,
22740     startY : 0,
22741     endX : 0,
22742     endY : 0,
22743     swiping : false,
22744     
22745     onTouchStart : function(e)
22746     {
22747         this.swiping = false;
22748         
22749         this.startX = e.browserEvent.touches[0].clientX;
22750         this.startY = e.browserEvent.touches[0].clientY;
22751     },
22752     
22753     onTouchMove : function(e)
22754     {
22755         this.swiping = true;
22756         
22757         this.endX = e.browserEvent.touches[0].clientX;
22758         this.endY = e.browserEvent.touches[0].clientY;
22759     },
22760     
22761     onTouchEnd : function(e)
22762     {
22763         if(!this.swiping){
22764             this.onClick(e);
22765             return;
22766         }
22767         
22768         var tabGroup = this.parent();
22769         
22770         if(this.endX > this.startX){ // swiping right
22771             tabGroup.showPanelPrev();
22772             return;
22773         }
22774         
22775         if(this.startX > this.endX){ // swiping left
22776             tabGroup.showPanelNext();
22777             return;
22778         }
22779     }
22780     
22781     
22782 });
22783  
22784
22785  
22786
22787  /*
22788  * - LGPL
22789  *
22790  * DateField
22791  * 
22792  */
22793
22794 /**
22795  * @class Roo.bootstrap.form.DateField
22796  * @extends Roo.bootstrap.form.Input
22797  * Bootstrap DateField class
22798  * @cfg {Number} weekStart default 0
22799  * @cfg {String} viewMode default empty, (months|years)
22800  * @cfg {String} minViewMode default empty, (months|years)
22801  * @cfg {Number} startDate default -Infinity
22802  * @cfg {Number} endDate default Infinity
22803  * @cfg {Boolean} todayHighlight default false
22804  * @cfg {Boolean} todayBtn default false
22805  * @cfg {Boolean} calendarWeeks default false
22806  * @cfg {Object} daysOfWeekDisabled default empty
22807  * @cfg {Boolean} singleMode default false (true | false)
22808  * 
22809  * @cfg {Boolean} keyboardNavigation default true
22810  * @cfg {String} language default en
22811  * 
22812  * @constructor
22813  * Create a new DateField
22814  * @param {Object} config The config object
22815  */
22816
22817 Roo.bootstrap.form.DateField = function(config){
22818     Roo.bootstrap.form.DateField.superclass.constructor.call(this, config);
22819      this.addEvents({
22820             /**
22821              * @event show
22822              * Fires when this field show.
22823              * @param {Roo.bootstrap.form.DateField} this
22824              * @param {Mixed} date The date value
22825              */
22826             show : true,
22827             /**
22828              * @event show
22829              * Fires when this field hide.
22830              * @param {Roo.bootstrap.form.DateField} this
22831              * @param {Mixed} date The date value
22832              */
22833             hide : true,
22834             /**
22835              * @event select
22836              * Fires when select a date.
22837              * @param {Roo.bootstrap.form.DateField} this
22838              * @param {Mixed} date The date value
22839              */
22840             select : true,
22841             /**
22842              * @event beforeselect
22843              * Fires when before select a date.
22844              * @param {Roo.bootstrap.form.DateField} this
22845              * @param {Mixed} date The date value
22846              */
22847             beforeselect : true
22848         });
22849 };
22850
22851 Roo.extend(Roo.bootstrap.form.DateField, Roo.bootstrap.form.Input,  {
22852     
22853     /**
22854      * @cfg {String} format
22855      * The default date format string which can be overriden for localization support.  The format must be
22856      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
22857      */
22858     format : "m/d/y",
22859     /**
22860      * @cfg {String} altFormats
22861      * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
22862      * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
22863      */
22864     altFormats : "m/d/Y|m-d-y|m-d-Y|m/d|m-d|md|mdy|mdY|d",
22865     
22866     weekStart : 0,
22867     
22868     viewMode : '',
22869     
22870     minViewMode : '',
22871     
22872     todayHighlight : false,
22873     
22874     todayBtn: false,
22875     
22876     language: 'en',
22877     
22878     keyboardNavigation: true,
22879     
22880     calendarWeeks: false,
22881     
22882     startDate: -Infinity,
22883     
22884     endDate: Infinity,
22885     
22886     daysOfWeekDisabled: [],
22887     
22888     _events: [],
22889     
22890     singleMode : false,
22891     
22892     UTCDate: function()
22893     {
22894         return new Date(Date.UTC.apply(Date, arguments));
22895     },
22896     
22897     UTCToday: function()
22898     {
22899         var today = new Date();
22900         return this.UTCDate(today.getUTCFullYear(), today.getUTCMonth(), today.getUTCDate());
22901     },
22902     
22903     getDate: function() {
22904             var d = this.getUTCDate();
22905             return new Date(d.getTime() + (d.getTimezoneOffset()*60000));
22906     },
22907     
22908     getUTCDate: function() {
22909             return this.date;
22910     },
22911     
22912     setDate: function(d) {
22913             this.setUTCDate(new Date(d.getTime() - (d.getTimezoneOffset()*60000)));
22914     },
22915     
22916     setUTCDate: function(d) {
22917             this.date = d;
22918             this.setValue(this.formatDate(this.date));
22919     },
22920         
22921     onRender: function(ct, position)
22922     {
22923         
22924         Roo.bootstrap.form.DateField.superclass.onRender.call(this, ct, position);
22925         
22926         this.language = this.language || 'en';
22927         this.language = this.language in Roo.bootstrap.form.DateField.dates ? this.language : this.language.split('-')[0];
22928         this.language = this.language in Roo.bootstrap.form.DateField.dates ? this.language : "en";
22929         
22930         this.isRTL = Roo.bootstrap.form.DateField.dates[this.language].rtl || false;
22931         this.format = this.format || 'm/d/y';
22932         this.isInline = false;
22933         this.isInput = true;
22934         this.component = this.el.select('.add-on', true).first() || false;
22935         this.component = (this.component && this.component.length === 0) ? false : this.component;
22936         this.hasInput = this.component && this.inputEl().length;
22937         
22938         if (typeof(this.minViewMode === 'string')) {
22939             switch (this.minViewMode) {
22940                 case 'months':
22941                     this.minViewMode = 1;
22942                     break;
22943                 case 'years':
22944                     this.minViewMode = 2;
22945                     break;
22946                 default:
22947                     this.minViewMode = 0;
22948                     break;
22949             }
22950         }
22951         
22952         if (typeof(this.viewMode === 'string')) {
22953             switch (this.viewMode) {
22954                 case 'months':
22955                     this.viewMode = 1;
22956                     break;
22957                 case 'years':
22958                     this.viewMode = 2;
22959                     break;
22960                 default:
22961                     this.viewMode = 0;
22962                     break;
22963             }
22964         }
22965                 
22966         this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.form.DateField.template);
22967         
22968 //        this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.form.DateField.template);
22969         
22970         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
22971         
22972         this.picker().on('mousedown', this.onMousedown, this);
22973         this.picker().on('click', this.onClick, this);
22974         
22975         this.picker().addClass('datepicker-dropdown');
22976         
22977         this.startViewMode = this.viewMode;
22978         
22979         if(this.singleMode){
22980             Roo.each(this.picker().select('thead > tr > th', true).elements, function(v){
22981                 v.setVisibilityMode(Roo.Element.DISPLAY);
22982                 v.hide();
22983             });
22984             
22985             Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
22986                 v.setStyle('width', '189px');
22987             });
22988         }
22989         
22990         Roo.each(this.picker().select('tfoot th.today', true).elements, function(v){
22991             if(!this.calendarWeeks){
22992                 v.remove();
22993                 return;
22994             }
22995             
22996             v.dom.innerHTML = Roo.bootstrap.form.DateField.dates[this.language].today;
22997             v.attr('colspan', function(i, val){
22998                 return parseInt(val) + 1;
22999             });
23000         });
23001                         
23002         
23003         this.weekEnd = this.weekStart === 0 ? 6 : this.weekStart - 1;
23004         
23005         this.setStartDate(this.startDate);
23006         this.setEndDate(this.endDate);
23007         
23008         this.setDaysOfWeekDisabled(this.daysOfWeekDisabled);
23009         
23010         this.fillDow();
23011         this.fillMonths();
23012         this.update();
23013         this.showMode();
23014         
23015         if(this.isInline) {
23016             this.showPopup();
23017         }
23018     },
23019     
23020     picker : function()
23021     {
23022         return this.pickerEl;
23023 //        return this.el.select('.datepicker', true).first();
23024     },
23025     
23026     fillDow: function()
23027     {
23028         var dowCnt = this.weekStart;
23029         
23030         var dow = {
23031             tag: 'tr',
23032             cn: [
23033                 
23034             ]
23035         };
23036         
23037         if(this.calendarWeeks){
23038             dow.cn.push({
23039                 tag: 'th',
23040                 cls: 'cw',
23041                 html: '&nbsp;'
23042             })
23043         }
23044         
23045         while (dowCnt < this.weekStart + 7) {
23046             dow.cn.push({
23047                 tag: 'th',
23048                 cls: 'dow',
23049                 html: Roo.bootstrap.form.DateField.dates[this.language].daysMin[(dowCnt++)%7]
23050             });
23051         }
23052         
23053         this.picker().select('>.datepicker-days thead', true).first().createChild(dow);
23054     },
23055     
23056     fillMonths: function()
23057     {    
23058         var i = 0;
23059         var months = this.picker().select('>.datepicker-months td', true).first();
23060         
23061         months.dom.innerHTML = '';
23062         
23063         while (i < 12) {
23064             var month = {
23065                 tag: 'span',
23066                 cls: 'month',
23067                 html: Roo.bootstrap.form.DateField.dates[this.language].monthsShort[i++]
23068             };
23069             
23070             months.createChild(month);
23071         }
23072         
23073     },
23074     
23075     update: function()
23076     {
23077         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;
23078         
23079         if (this.date < this.startDate) {
23080             this.viewDate = new Date(this.startDate);
23081         } else if (this.date > this.endDate) {
23082             this.viewDate = new Date(this.endDate);
23083         } else {
23084             this.viewDate = new Date(this.date);
23085         }
23086         
23087         this.fill();
23088     },
23089     
23090     fill: function() 
23091     {
23092         var d = new Date(this.viewDate),
23093                 year = d.getUTCFullYear(),
23094                 month = d.getUTCMonth(),
23095                 startYear = this.startDate !== -Infinity ? this.startDate.getUTCFullYear() : -Infinity,
23096                 startMonth = this.startDate !== -Infinity ? this.startDate.getUTCMonth() : -Infinity,
23097                 endYear = this.endDate !== Infinity ? this.endDate.getUTCFullYear() : Infinity,
23098                 endMonth = this.endDate !== Infinity ? this.endDate.getUTCMonth() : Infinity,
23099                 currentDate = this.date && this.date.valueOf(),
23100                 today = this.UTCToday();
23101         
23102         this.picker().select('>.datepicker-days thead th.switch', true).first().dom.innerHTML = Roo.bootstrap.form.DateField.dates[this.language].months[month]+' '+year;
23103         
23104 //        this.picker().select('>tfoot th.today', true).first().dom.innerHTML = Roo.bootstrap.form.DateField.dates[this.language].today;
23105         
23106 //        this.picker.select('>tfoot th.today').
23107 //                                              .text(dates[this.language].today)
23108 //                                              .toggle(this.todayBtn !== false);
23109     
23110         this.updateNavArrows();
23111         this.fillMonths();
23112                                                 
23113         var prevMonth = this.UTCDate(year, month-1, 28,0,0,0,0),
23114         
23115         day = prevMonth.getDaysInMonth(prevMonth.getUTCFullYear(), prevMonth.getUTCMonth());
23116          
23117         prevMonth.setUTCDate(day);
23118         
23119         prevMonth.setUTCDate(day - (prevMonth.getUTCDay() - this.weekStart + 7)%7);
23120         
23121         var nextMonth = new Date(prevMonth);
23122         
23123         nextMonth.setUTCDate(nextMonth.getUTCDate() + 42);
23124         
23125         nextMonth = nextMonth.valueOf();
23126         
23127         var fillMonths = false;
23128         
23129         this.picker().select('>.datepicker-days tbody',true).first().dom.innerHTML = '';
23130         
23131         while(prevMonth.valueOf() <= nextMonth) {
23132             var clsName = '';
23133             
23134             if (prevMonth.getUTCDay() === this.weekStart) {
23135                 if(fillMonths){
23136                     this.picker().select('>.datepicker-days tbody',true).first().createChild(fillMonths);
23137                 }
23138                     
23139                 fillMonths = {
23140                     tag: 'tr',
23141                     cn: []
23142                 };
23143                 
23144                 if(this.calendarWeeks){
23145                     // ISO 8601: First week contains first thursday.
23146                     // ISO also states week starts on Monday, but we can be more abstract here.
23147                     var
23148                     // Start of current week: based on weekstart/current date
23149                     ws = new Date(+prevMonth + (this.weekStart - prevMonth.getUTCDay() - 7) % 7 * 864e5),
23150                     // Thursday of this week
23151                     th = new Date(+ws + (7 + 4 - ws.getUTCDay()) % 7 * 864e5),
23152                     // First Thursday of year, year from thursday
23153                     yth = new Date(+(yth = this.UTCDate(th.getUTCFullYear(), 0, 1)) + (7 + 4 - yth.getUTCDay())%7*864e5),
23154                     // Calendar week: ms between thursdays, div ms per day, div 7 days
23155                     calWeek =  (th - yth) / 864e5 / 7 + 1;
23156                     
23157                     fillMonths.cn.push({
23158                         tag: 'td',
23159                         cls: 'cw',
23160                         html: calWeek
23161                     });
23162                 }
23163             }
23164             
23165             if (prevMonth.getUTCFullYear() < year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() < month)) {
23166                 clsName += ' old';
23167             } else if (prevMonth.getUTCFullYear() > year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() > month)) {
23168                 clsName += ' new';
23169             }
23170             if (this.todayHighlight &&
23171                 prevMonth.getUTCFullYear() == today.getFullYear() &&
23172                 prevMonth.getUTCMonth() == today.getMonth() &&
23173                 prevMonth.getUTCDate() == today.getDate()) {
23174                 clsName += ' today';
23175             }
23176             
23177             if (currentDate && prevMonth.valueOf() === currentDate) {
23178                 clsName += ' active';
23179             }
23180             
23181             if (prevMonth.valueOf() < this.startDate || prevMonth.valueOf() > this.endDate ||
23182                     this.daysOfWeekDisabled.indexOf(prevMonth.getUTCDay()) !== -1) {
23183                     clsName += ' disabled';
23184             }
23185             
23186             fillMonths.cn.push({
23187                 tag: 'td',
23188                 cls: 'day ' + clsName,
23189                 html: prevMonth.getDate()
23190             });
23191             
23192             prevMonth.setDate(prevMonth.getDate()+1);
23193         }
23194           
23195         var currentYear = this.date && this.date.getUTCFullYear();
23196         var currentMonth = this.date && this.date.getUTCMonth();
23197         
23198         this.picker().select('>.datepicker-months th.switch',true).first().dom.innerHTML = year;
23199         
23200         Roo.each(this.picker().select('>.datepicker-months tbody span',true).elements, function(v,k){
23201             v.removeClass('active');
23202             
23203             if(currentYear === year && k === currentMonth){
23204                 v.addClass('active');
23205             }
23206             
23207             if (year < startYear || year > endYear || (year == startYear && k < startMonth) || (year == endYear && k > endMonth)) {
23208                 v.addClass('disabled');
23209             }
23210             
23211         });
23212         
23213         
23214         year = parseInt(year/10, 10) * 10;
23215         
23216         this.picker().select('>.datepicker-years th.switch', true).first().dom.innerHTML = year + '-' + (year + 9);
23217         
23218         this.picker().select('>.datepicker-years tbody td',true).first().dom.innerHTML = '';
23219         
23220         year -= 1;
23221         for (var i = -1; i < 11; i++) {
23222             this.picker().select('>.datepicker-years tbody td',true).first().createChild({
23223                 tag: 'span',
23224                 cls: 'year' + (i === -1 || i === 10 ? ' old' : '') + (currentYear === year ? ' active' : '') + (year < startYear || year > endYear ? ' disabled' : ''),
23225                 html: year
23226             });
23227             
23228             year += 1;
23229         }
23230     },
23231     
23232     showMode: function(dir) 
23233     {
23234         if (dir) {
23235             this.viewMode = Math.max(this.minViewMode, Math.min(2, this.viewMode + dir));
23236         }
23237         
23238         Roo.each(this.picker().select('>div',true).elements, function(v){
23239             v.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
23240             v.hide();
23241         });
23242         this.picker().select('>.datepicker-'+Roo.bootstrap.form.DateField.modes[this.viewMode].clsName, true).first().show();
23243     },
23244     
23245     place: function()
23246     {
23247         if(this.isInline) {
23248             return;
23249         }
23250         
23251         this.picker().removeClass(['bottom', 'top']);
23252         
23253         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
23254             /*
23255              * place to the top of element!
23256              *
23257              */
23258             
23259             this.picker().addClass('top');
23260             this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
23261             
23262             return;
23263         }
23264         
23265         this.picker().addClass('bottom');
23266         
23267         this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
23268     },
23269     
23270     parseDate : function(value)
23271     {
23272         if(!value || value instanceof Date){
23273             return value;
23274         }
23275         var v = Date.parseDate(value, this.format);
23276         if (!v && (this.useIso || value.match(/^(\d{4})-0?(\d+)-0?(\d+)/))) {
23277             v = Date.parseDate(value, 'Y-m-d');
23278         }
23279         if(!v && this.altFormats){
23280             if(!this.altFormatsArray){
23281                 this.altFormatsArray = this.altFormats.split("|");
23282             }
23283             for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
23284                 v = Date.parseDate(value, this.altFormatsArray[i]);
23285             }
23286         }
23287         return v;
23288     },
23289     
23290     formatDate : function(date, fmt)
23291     {   
23292         return (!date || !(date instanceof Date)) ?
23293         date : date.dateFormat(fmt || this.format);
23294     },
23295     
23296     onFocus : function()
23297     {
23298         Roo.bootstrap.form.DateField.superclass.onFocus.call(this);
23299         this.showPopup();
23300     },
23301     
23302     onBlur : function()
23303     {
23304         Roo.bootstrap.form.DateField.superclass.onBlur.call(this);
23305         
23306         var d = this.inputEl().getValue();
23307         
23308         this.setValue(d);
23309                 
23310         this.hidePopup();
23311     },
23312     
23313     showPopup : function()
23314     {
23315         this.picker().show();
23316         this.update();
23317         this.place();
23318         
23319         this.fireEvent('showpopup', this, this.date);
23320     },
23321     
23322     hidePopup : function()
23323     {
23324         if(this.isInline) {
23325             return;
23326         }
23327         this.picker().hide();
23328         this.viewMode = this.startViewMode;
23329         this.showMode();
23330         
23331         this.fireEvent('hidepopup', this, this.date);
23332         
23333     },
23334     
23335     onMousedown: function(e)
23336     {
23337         e.stopPropagation();
23338         e.preventDefault();
23339     },
23340     
23341     keyup: function(e)
23342     {
23343         Roo.bootstrap.form.DateField.superclass.keyup.call(this);
23344         this.update();
23345     },
23346
23347     setValue: function(v)
23348     {
23349         if(this.fireEvent('beforeselect', this, v) !== false){
23350             var d = new Date(this.parseDate(v) ).clearTime();
23351         
23352             if(isNaN(d.getTime())){
23353                 this.date = this.viewDate = '';
23354                 Roo.bootstrap.form.DateField.superclass.setValue.call(this, '');
23355                 return;
23356             }
23357
23358             v = this.formatDate(d);
23359
23360             Roo.bootstrap.form.DateField.superclass.setValue.call(this, v);
23361
23362             this.date = new Date(d.getTime() - d.getTimezoneOffset()*60000);
23363
23364             this.update();
23365
23366             this.fireEvent('select', this, this.date);
23367         }
23368     },
23369     
23370     getValue: function()
23371     {
23372         return this.formatDate(this.date);
23373     },
23374     
23375     fireKey: function(e)
23376     {
23377         if (!this.picker().isVisible()){
23378             if (e.keyCode == 27) { // allow escape to hide and re-show picker
23379                 this.showPopup();
23380             }
23381             return;
23382         }
23383         
23384         var dateChanged = false,
23385         dir, day, month,
23386         newDate, newViewDate;
23387         
23388         switch(e.keyCode){
23389             case 27: // escape
23390                 this.hidePopup();
23391                 e.preventDefault();
23392                 break;
23393             case 37: // left
23394             case 39: // right
23395                 if (!this.keyboardNavigation) {
23396                     break;
23397                 }
23398                 dir = e.keyCode == 37 ? -1 : 1;
23399                 
23400                 if (e.ctrlKey){
23401                     newDate = this.moveYear(this.date, dir);
23402                     newViewDate = this.moveYear(this.viewDate, dir);
23403                 } else if (e.shiftKey){
23404                     newDate = this.moveMonth(this.date, dir);
23405                     newViewDate = this.moveMonth(this.viewDate, dir);
23406                 } else {
23407                     newDate = new Date(this.date);
23408                     newDate.setUTCDate(this.date.getUTCDate() + dir);
23409                     newViewDate = new Date(this.viewDate);
23410                     newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir);
23411                 }
23412                 if (this.dateWithinRange(newDate)){
23413                     this.date = newDate;
23414                     this.viewDate = newViewDate;
23415                     this.setValue(this.formatDate(this.date));
23416 //                    this.update();
23417                     e.preventDefault();
23418                     dateChanged = true;
23419                 }
23420                 break;
23421             case 38: // up
23422             case 40: // down
23423                 if (!this.keyboardNavigation) {
23424                     break;
23425                 }
23426                 dir = e.keyCode == 38 ? -1 : 1;
23427                 if (e.ctrlKey){
23428                     newDate = this.moveYear(this.date, dir);
23429                     newViewDate = this.moveYear(this.viewDate, dir);
23430                 } else if (e.shiftKey){
23431                     newDate = this.moveMonth(this.date, dir);
23432                     newViewDate = this.moveMonth(this.viewDate, dir);
23433                 } else {
23434                     newDate = new Date(this.date);
23435                     newDate.setUTCDate(this.date.getUTCDate() + dir * 7);
23436                     newViewDate = new Date(this.viewDate);
23437                     newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir * 7);
23438                 }
23439                 if (this.dateWithinRange(newDate)){
23440                     this.date = newDate;
23441                     this.viewDate = newViewDate;
23442                     this.setValue(this.formatDate(this.date));
23443 //                    this.update();
23444                     e.preventDefault();
23445                     dateChanged = true;
23446                 }
23447                 break;
23448             case 13: // enter
23449                 this.setValue(this.formatDate(this.date));
23450                 this.hidePopup();
23451                 e.preventDefault();
23452                 break;
23453             case 9: // tab
23454                 this.setValue(this.formatDate(this.date));
23455                 this.hidePopup();
23456                 break;
23457             case 16: // shift
23458             case 17: // ctrl
23459             case 18: // alt
23460                 break;
23461             default :
23462                 this.hidePopup();
23463                 
23464         }
23465     },
23466     
23467     
23468     onClick: function(e) 
23469     {
23470         e.stopPropagation();
23471         e.preventDefault();
23472         
23473         var target = e.getTarget();
23474         
23475         if(target.nodeName.toLowerCase() === 'i'){
23476             target = Roo.get(target).dom.parentNode;
23477         }
23478         
23479         var nodeName = target.nodeName;
23480         var className = target.className;
23481         var html = target.innerHTML;
23482         //Roo.log(nodeName);
23483         
23484         switch(nodeName.toLowerCase()) {
23485             case 'th':
23486                 switch(className) {
23487                     case 'switch':
23488                         this.showMode(1);
23489                         break;
23490                     case 'prev':
23491                     case 'next':
23492                         var dir = Roo.bootstrap.form.DateField.modes[this.viewMode].navStep * (className == 'prev' ? -1 : 1);
23493                         switch(this.viewMode){
23494                                 case 0:
23495                                         this.viewDate = this.moveMonth(this.viewDate, dir);
23496                                         break;
23497                                 case 1:
23498                                 case 2:
23499                                         this.viewDate = this.moveYear(this.viewDate, dir);
23500                                         break;
23501                         }
23502                         this.fill();
23503                         break;
23504                     case 'today':
23505                         var date = new Date();
23506                         this.date = this.UTCDate(date.getFullYear(), date.getMonth(), date.getDate(), 0, 0, 0);
23507 //                        this.fill()
23508                         this.setValue(this.formatDate(this.date));
23509                         
23510                         this.hidePopup();
23511                         break;
23512                 }
23513                 break;
23514             case 'span':
23515                 if (className.indexOf('disabled') < 0) {
23516                 if (!this.viewDate) {
23517                     this.viewDate = new Date();
23518                 }
23519                 this.viewDate.setUTCDate(1);
23520                     if (className.indexOf('month') > -1) {
23521                         this.viewDate.setUTCMonth(Roo.bootstrap.form.DateField.dates[this.language].monthsShort.indexOf(html));
23522                     } else {
23523                         var year = parseInt(html, 10) || 0;
23524                         this.viewDate.setUTCFullYear(year);
23525                         
23526                     }
23527                     
23528                     if(this.singleMode){
23529                         this.setValue(this.formatDate(this.viewDate));
23530                         this.hidePopup();
23531                         return;
23532                     }
23533                     
23534                     this.showMode(-1);
23535                     this.fill();
23536                 }
23537                 break;
23538                 
23539             case 'td':
23540                 //Roo.log(className);
23541                 if (className.indexOf('day') > -1 && className.indexOf('disabled') < 0 ){
23542                     var day = parseInt(html, 10) || 1;
23543                     var year =  (this.viewDate || new Date()).getUTCFullYear(),
23544                         month = (this.viewDate || new Date()).getUTCMonth();
23545
23546                     if (className.indexOf('old') > -1) {
23547                         if(month === 0 ){
23548                             month = 11;
23549                             year -= 1;
23550                         }else{
23551                             month -= 1;
23552                         }
23553                     } else if (className.indexOf('new') > -1) {
23554                         if (month == 11) {
23555                             month = 0;
23556                             year += 1;
23557                         } else {
23558                             month += 1;
23559                         }
23560                     }
23561                     //Roo.log([year,month,day]);
23562                     this.date = this.UTCDate(year, month, day,0,0,0,0);
23563                     this.viewDate = this.UTCDate(year, month, Math.min(28, day),0,0,0,0);
23564 //                    this.fill();
23565                     //Roo.log(this.formatDate(this.date));
23566                     this.setValue(this.formatDate(this.date));
23567                     this.hidePopup();
23568                 }
23569                 break;
23570         }
23571     },
23572     
23573     setStartDate: function(startDate)
23574     {
23575         this.startDate = startDate || -Infinity;
23576         if (this.startDate !== -Infinity) {
23577             this.startDate = this.parseDate(this.startDate);
23578         }
23579         this.update();
23580         this.updateNavArrows();
23581     },
23582
23583     setEndDate: function(endDate)
23584     {
23585         this.endDate = endDate || Infinity;
23586         if (this.endDate !== Infinity) {
23587             this.endDate = this.parseDate(this.endDate);
23588         }
23589         this.update();
23590         this.updateNavArrows();
23591     },
23592     
23593     setDaysOfWeekDisabled: function(daysOfWeekDisabled)
23594     {
23595         this.daysOfWeekDisabled = daysOfWeekDisabled || [];
23596         if (typeof(this.daysOfWeekDisabled) !== 'object') {
23597             this.daysOfWeekDisabled = this.daysOfWeekDisabled.split(/,\s*/);
23598         }
23599         this.daysOfWeekDisabled = this.daysOfWeekDisabled.map(function (d) {
23600             return parseInt(d, 10);
23601         });
23602         this.update();
23603         this.updateNavArrows();
23604     },
23605     
23606     updateNavArrows: function() 
23607     {
23608         if(this.singleMode){
23609             return;
23610         }
23611         
23612         var d = new Date(this.viewDate),
23613         year = d.getUTCFullYear(),
23614         month = d.getUTCMonth();
23615         
23616         Roo.each(this.picker().select('.prev', true).elements, function(v){
23617             v.show();
23618             switch (this.viewMode) {
23619                 case 0:
23620
23621                     if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear() && month <= this.startDate.getUTCMonth()) {
23622                         v.hide();
23623                     }
23624                     break;
23625                 case 1:
23626                 case 2:
23627                     if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear()) {
23628                         v.hide();
23629                     }
23630                     break;
23631             }
23632         });
23633         
23634         Roo.each(this.picker().select('.next', true).elements, function(v){
23635             v.show();
23636             switch (this.viewMode) {
23637                 case 0:
23638
23639                     if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear() && month >= this.endDate.getUTCMonth()) {
23640                         v.hide();
23641                     }
23642                     break;
23643                 case 1:
23644                 case 2:
23645                     if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear()) {
23646                         v.hide();
23647                     }
23648                     break;
23649             }
23650         })
23651     },
23652     
23653     moveMonth: function(date, dir)
23654     {
23655         if (!dir) {
23656             return date;
23657         }
23658         var new_date = new Date(date.valueOf()),
23659         day = new_date.getUTCDate(),
23660         month = new_date.getUTCMonth(),
23661         mag = Math.abs(dir),
23662         new_month, test;
23663         dir = dir > 0 ? 1 : -1;
23664         if (mag == 1){
23665             test = dir == -1
23666             // If going back one month, make sure month is not current month
23667             // (eg, Mar 31 -> Feb 31 == Feb 28, not Mar 02)
23668             ? function(){
23669                 return new_date.getUTCMonth() == month;
23670             }
23671             // If going forward one month, make sure month is as expected
23672             // (eg, Jan 31 -> Feb 31 == Feb 28, not Mar 02)
23673             : function(){
23674                 return new_date.getUTCMonth() != new_month;
23675             };
23676             new_month = month + dir;
23677             new_date.setUTCMonth(new_month);
23678             // Dec -> Jan (12) or Jan -> Dec (-1) -- limit expected date to 0-11
23679             if (new_month < 0 || new_month > 11) {
23680                 new_month = (new_month + 12) % 12;
23681             }
23682         } else {
23683             // For magnitudes >1, move one month at a time...
23684             for (var i=0; i<mag; i++) {
23685                 // ...which might decrease the day (eg, Jan 31 to Feb 28, etc)...
23686                 new_date = this.moveMonth(new_date, dir);
23687             }
23688             // ...then reset the day, keeping it in the new month
23689             new_month = new_date.getUTCMonth();
23690             new_date.setUTCDate(day);
23691             test = function(){
23692                 return new_month != new_date.getUTCMonth();
23693             };
23694         }
23695         // Common date-resetting loop -- if date is beyond end of month, make it
23696         // end of month
23697         while (test()){
23698             new_date.setUTCDate(--day);
23699             new_date.setUTCMonth(new_month);
23700         }
23701         return new_date;
23702     },
23703
23704     moveYear: function(date, dir)
23705     {
23706         return this.moveMonth(date, dir*12);
23707     },
23708
23709     dateWithinRange: function(date)
23710     {
23711         return date >= this.startDate && date <= this.endDate;
23712     },
23713
23714     
23715     remove: function() 
23716     {
23717         this.picker().remove();
23718     },
23719     
23720     validateValue : function(value)
23721     {
23722         if(this.getVisibilityEl().hasClass('hidden')){
23723             return true;
23724         }
23725         
23726         if(value.length < 1)  {
23727             if(this.allowBlank){
23728                 return true;
23729             }
23730             return false;
23731         }
23732         
23733         if(value.length < this.minLength){
23734             return false;
23735         }
23736         if(value.length > this.maxLength){
23737             return false;
23738         }
23739         if(this.vtype){
23740             var vt = Roo.form.VTypes;
23741             if(!vt[this.vtype](value, this)){
23742                 return false;
23743             }
23744         }
23745         if(typeof this.validator == "function"){
23746             var msg = this.validator(value);
23747             if(msg !== true){
23748                 return false;
23749             }
23750         }
23751         
23752         if(this.regex && !this.regex.test(value)){
23753             return false;
23754         }
23755         
23756         if(typeof(this.parseDate(value)) == 'undefined'){
23757             return false;
23758         }
23759         
23760         if (this.endDate !== Infinity && this.parseDate(value).getTime() > this.endDate.getTime()) {
23761             return false;
23762         }      
23763         
23764         if (this.startDate !== -Infinity && this.parseDate(value).getTime() < this.startDate.getTime()) {
23765             return false;
23766         } 
23767         
23768         
23769         return true;
23770     },
23771     
23772     reset : function()
23773     {
23774         this.date = this.viewDate = '';
23775         
23776         Roo.bootstrap.form.DateField.superclass.setValue.call(this, '');
23777     }
23778    
23779 });
23780
23781 Roo.apply(Roo.bootstrap.form.DateField,  {
23782     
23783     head : {
23784         tag: 'thead',
23785         cn: [
23786         {
23787             tag: 'tr',
23788             cn: [
23789             {
23790                 tag: 'th',
23791                 cls: 'prev',
23792                 html: '<i class="fa fa-arrow-left"/>'
23793             },
23794             {
23795                 tag: 'th',
23796                 cls: 'switch',
23797                 colspan: '5'
23798             },
23799             {
23800                 tag: 'th',
23801                 cls: 'next',
23802                 html: '<i class="fa fa-arrow-right"/>'
23803             }
23804
23805             ]
23806         }
23807         ]
23808     },
23809     
23810     content : {
23811         tag: 'tbody',
23812         cn: [
23813         {
23814             tag: 'tr',
23815             cn: [
23816             {
23817                 tag: 'td',
23818                 colspan: '7'
23819             }
23820             ]
23821         }
23822         ]
23823     },
23824     
23825     footer : {
23826         tag: 'tfoot',
23827         cn: [
23828         {
23829             tag: 'tr',
23830             cn: [
23831             {
23832                 tag: 'th',
23833                 colspan: '7',
23834                 cls: 'today'
23835             }
23836                     
23837             ]
23838         }
23839         ]
23840     },
23841     
23842     dates:{
23843         en: {
23844             days: ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday"],
23845             daysShort: ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"],
23846             daysMin: ["Su", "Mo", "Tu", "We", "Th", "Fr", "Sa", "Su"],
23847             months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
23848             monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"],
23849             today: "Today"
23850         }
23851     },
23852     
23853     modes: [
23854     {
23855         clsName: 'days',
23856         navFnc: 'Month',
23857         navStep: 1
23858     },
23859     {
23860         clsName: 'months',
23861         navFnc: 'FullYear',
23862         navStep: 1
23863     },
23864     {
23865         clsName: 'years',
23866         navFnc: 'FullYear',
23867         navStep: 10
23868     }]
23869 });
23870
23871 Roo.apply(Roo.bootstrap.form.DateField,  {
23872   
23873     template : {
23874         tag: 'div',
23875         cls: 'datepicker dropdown-menu roo-dynamic shadow',
23876         cn: [
23877         {
23878             tag: 'div',
23879             cls: 'datepicker-days',
23880             cn: [
23881             {
23882                 tag: 'table',
23883                 cls: 'table-condensed',
23884                 cn:[
23885                 Roo.bootstrap.form.DateField.head,
23886                 {
23887                     tag: 'tbody'
23888                 },
23889                 Roo.bootstrap.form.DateField.footer
23890                 ]
23891             }
23892             ]
23893         },
23894         {
23895             tag: 'div',
23896             cls: 'datepicker-months',
23897             cn: [
23898             {
23899                 tag: 'table',
23900                 cls: 'table-condensed',
23901                 cn:[
23902                 Roo.bootstrap.form.DateField.head,
23903                 Roo.bootstrap.form.DateField.content,
23904                 Roo.bootstrap.form.DateField.footer
23905                 ]
23906             }
23907             ]
23908         },
23909         {
23910             tag: 'div',
23911             cls: 'datepicker-years',
23912             cn: [
23913             {
23914                 tag: 'table',
23915                 cls: 'table-condensed',
23916                 cn:[
23917                 Roo.bootstrap.form.DateField.head,
23918                 Roo.bootstrap.form.DateField.content,
23919                 Roo.bootstrap.form.DateField.footer
23920                 ]
23921             }
23922             ]
23923         }
23924         ]
23925     }
23926 });
23927
23928  
23929
23930  /*
23931  * - LGPL
23932  *
23933  * TimeField
23934  * 
23935  */
23936
23937 /**
23938  * @class Roo.bootstrap.form.TimeField
23939  * @extends Roo.bootstrap.form.Input
23940  * Bootstrap DateField class
23941  * @cfg {Number} minuteStep the minutes is always the multiple of a fixed number, default 1
23942  * 
23943  * 
23944  * @constructor
23945  * Create a new TimeField
23946  * @param {Object} config The config object
23947  */
23948
23949 Roo.bootstrap.form.TimeField = function(config){
23950     Roo.bootstrap.form.TimeField.superclass.constructor.call(this, config);
23951     this.addEvents({
23952             /**
23953              * @event show
23954              * Fires when this field show.
23955              * @param {Roo.bootstrap.form.DateField} thisthis
23956              * @param {Mixed} date The date value
23957              */
23958             show : true,
23959             /**
23960              * @event show
23961              * Fires when this field hide.
23962              * @param {Roo.bootstrap.form.DateField} this
23963              * @param {Mixed} date The date value
23964              */
23965             hide : true,
23966             /**
23967              * @event select
23968              * Fires when select a date.
23969              * @param {Roo.bootstrap.form.DateField} this
23970              * @param {Mixed} date The date value
23971              */
23972             select : true
23973         });
23974 };
23975
23976 Roo.extend(Roo.bootstrap.form.TimeField, Roo.bootstrap.form.Input,  {
23977     
23978     /**
23979      * @cfg {String} format
23980      * The default time format string which can be overriden for localization support.  The format must be
23981      * valid according to {@link Date#parseDate} (defaults to 'H:i').
23982      */
23983     format : "H:i",
23984     minuteStep : 1,
23985
23986     getAutoCreate : function()
23987     {
23988         this.after = '<i class="fa far fa-clock"></i>';
23989         return Roo.bootstrap.form.TimeField.superclass.getAutoCreate.call(this);
23990         
23991          
23992     },
23993     onRender: function(ct, position)
23994     {
23995         
23996         Roo.bootstrap.form.TimeField.superclass.onRender.call(this, ct, position);
23997                 
23998         this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.form.TimeField.template);
23999         
24000         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
24001         
24002         this.pop = this.picker().select('>.datepicker-time',true).first();
24003         this.pop.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
24004         
24005         this.picker().on('mousedown', this.onMousedown, this);
24006         this.picker().on('click', this.onClick, this);
24007         
24008         this.picker().addClass('datepicker-dropdown');
24009     
24010         this.fillTime();
24011         this.update();
24012             
24013         this.pop.select('.hours-up', true).first().on('click', this.onIncrementHours, this);
24014         this.pop.select('.hours-down', true).first().on('click', this.onDecrementHours, this);
24015         this.pop.select('.minutes-up', true).first().on('click', this.onIncrementMinutes, this);
24016         this.pop.select('.minutes-down', true).first().on('click', this.onDecrementMinutes, this);
24017         this.pop.select('button.period', true).first().on('click', this.onTogglePeriod, this);
24018         this.pop.select('button.ok', true).first().on('click', this.setTime, this);
24019
24020     },
24021     
24022     fireKey: function(e){
24023         if (!this.picker().isVisible()){
24024             if (e.keyCode == 27) { // allow escape to hide and re-show picker
24025                 this.show();
24026             }
24027             return;
24028         }
24029
24030         e.preventDefault();
24031         
24032         switch(e.keyCode){
24033             case 27: // escape
24034                 this.hide();
24035                 break;
24036             case 37: // left
24037             case 39: // right
24038                 this.onTogglePeriod();
24039                 break;
24040             case 38: // up
24041                 this.onIncrementMinutes();
24042                 break;
24043             case 40: // down
24044                 this.onDecrementMinutes();
24045                 break;
24046             case 13: // enter
24047             case 9: // tab
24048                 this.setTime();
24049                 break;
24050         }
24051     },
24052     
24053     onClick: function(e) {
24054         e.stopPropagation();
24055         e.preventDefault();
24056     },
24057     
24058     picker : function()
24059     {
24060         return this.pickerEl;
24061     },
24062     
24063     fillTime: function()
24064     {    
24065         var time = this.pop.select('tbody', true).first();
24066         
24067         time.dom.innerHTML = '';
24068         
24069         time.createChild({
24070             tag: 'tr',
24071             cn: [
24072                 {
24073                     tag: 'td',
24074                     cn: [
24075                         {
24076                             tag: 'a',
24077                             href: '#',
24078                             cls: 'btn',
24079                             cn: [
24080                                 {
24081                                     tag: 'i',
24082                                     cls: 'hours-up fa fas fa-chevron-up'
24083                                 }
24084                             ]
24085                         } 
24086                     ]
24087                 },
24088                 {
24089                     tag: 'td',
24090                     cls: 'separator'
24091                 },
24092                 {
24093                     tag: 'td',
24094                     cn: [
24095                         {
24096                             tag: 'a',
24097                             href: '#',
24098                             cls: 'btn',
24099                             cn: [
24100                                 {
24101                                     tag: 'i',
24102                                     cls: 'minutes-up fa fas fa-chevron-up'
24103                                 }
24104                             ]
24105                         }
24106                     ]
24107                 },
24108                 {
24109                     tag: 'td',
24110                     cls: 'separator'
24111                 }
24112             ]
24113         });
24114         
24115         time.createChild({
24116             tag: 'tr',
24117             cn: [
24118                 {
24119                     tag: 'td',
24120                     cn: [
24121                         {
24122                             tag: 'span',
24123                             cls: 'timepicker-hour',
24124                             html: '00'
24125                         }  
24126                     ]
24127                 },
24128                 {
24129                     tag: 'td',
24130                     cls: 'separator',
24131                     html: ':'
24132                 },
24133                 {
24134                     tag: 'td',
24135                     cn: [
24136                         {
24137                             tag: 'span',
24138                             cls: 'timepicker-minute',
24139                             html: '00'
24140                         }  
24141                     ]
24142                 },
24143                 {
24144                     tag: 'td',
24145                     cls: 'separator'
24146                 },
24147                 {
24148                     tag: 'td',
24149                     cn: [
24150                         {
24151                             tag: 'button',
24152                             type: 'button',
24153                             cls: 'btn btn-primary period',
24154                             html: 'AM'
24155                             
24156                         }
24157                     ]
24158                 }
24159             ]
24160         });
24161         
24162         time.createChild({
24163             tag: 'tr',
24164             cn: [
24165                 {
24166                     tag: 'td',
24167                     cn: [
24168                         {
24169                             tag: 'a',
24170                             href: '#',
24171                             cls: 'btn',
24172                             cn: [
24173                                 {
24174                                     tag: 'span',
24175                                     cls: 'hours-down fa fas fa-chevron-down'
24176                                 }
24177                             ]
24178                         }
24179                     ]
24180                 },
24181                 {
24182                     tag: 'td',
24183                     cls: 'separator'
24184                 },
24185                 {
24186                     tag: 'td',
24187                     cn: [
24188                         {
24189                             tag: 'a',
24190                             href: '#',
24191                             cls: 'btn',
24192                             cn: [
24193                                 {
24194                                     tag: 'span',
24195                                     cls: 'minutes-down fa fas fa-chevron-down'
24196                                 }
24197                             ]
24198                         }
24199                     ]
24200                 },
24201                 {
24202                     tag: 'td',
24203                     cls: 'separator'
24204                 }
24205             ]
24206         });
24207         
24208     },
24209     
24210     update: function()
24211     {
24212         // default minute is a multiple of minuteStep
24213         if(typeof(this.time) === 'undefined') {
24214             this.time = new Date();
24215             this.time = this.time.add(Date.MINUTE, Math.round(parseInt(this.time.format('i')) / this.minuteStep) * this.minuteStep - parseInt(this.time.format('i')));
24216         }
24217         this.time = (typeof(this.time) === 'undefined') ? new Date() : this.time;
24218         
24219         this.fill();
24220     },
24221     
24222     fill: function() 
24223     {
24224         var hours = this.time.getHours();
24225         var minutes = this.time.getMinutes();
24226         var period = 'AM';
24227         
24228         if(hours > 11){
24229             period = 'PM';
24230         }
24231         
24232         if(hours == 0){
24233             hours = 12;
24234         }
24235         
24236         
24237         if(hours > 12){
24238             hours = hours - 12;
24239         }
24240         
24241         if(hours < 10){
24242             hours = '0' + hours;
24243         }
24244         
24245         if(minutes < 10){
24246             minutes = '0' + minutes;
24247         }
24248         
24249         this.pop.select('.timepicker-hour', true).first().dom.innerHTML = hours;
24250         this.pop.select('.timepicker-minute', true).first().dom.innerHTML = minutes;
24251         this.pop.select('button', true).first().dom.innerHTML = period;
24252         
24253     },
24254     
24255     place: function()
24256     {   
24257         this.picker().removeClass(['bottom-left', 'bottom-right', 'top-left', 'top-right']);
24258         
24259         var cls = ['bottom'];
24260         
24261         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){ // top
24262             cls.pop();
24263             cls.push('top');
24264         }
24265         
24266         cls.push('right');
24267         
24268         if((Roo.lib.Dom.getViewWidth() + Roo.get(document.body).getScroll().left) - (this.inputEl().getLeft() + this.picker().getWidth()) < 0){ // left
24269             cls.pop();
24270             cls.push('left');
24271         }
24272         //this.picker().setXY(20000,20000);
24273         this.picker().addClass(cls.join('-'));
24274         
24275         var _this = this;
24276         
24277         Roo.each(cls, function(c){
24278             if(c == 'bottom'){
24279                 (function() {
24280                  //  
24281                 }).defer(200);
24282                  _this.picker().alignTo(_this.inputEl(),   "tr-br", [0, 10], false);
24283                 //_this.picker().setTop(_this.inputEl().getHeight());
24284                 return;
24285             }
24286             if(c == 'top'){
24287                  _this.picker().alignTo(_this.inputEl(),   "br-tr", [0, 10], false);
24288                 
24289                 //_this.picker().setTop(0 - _this.picker().getHeight());
24290                 return;
24291             }
24292             /*
24293             if(c == 'left'){
24294                 _this.picker().setLeft(_this.inputEl().getLeft() + _this.inputEl().getWidth() - _this.el.getLeft() - _this.picker().getWidth());
24295                 return;
24296             }
24297             if(c == 'right'){
24298                 _this.picker().setLeft(_this.inputEl().getLeft() - _this.el.getLeft());
24299                 return;
24300             }
24301             */
24302         });
24303         
24304     },
24305   
24306     onFocus : function()
24307     {
24308         Roo.bootstrap.form.TimeField.superclass.onFocus.call(this);
24309         this.show();
24310     },
24311     
24312     onBlur : function()
24313     {
24314         Roo.bootstrap.form.TimeField.superclass.onBlur.call(this);
24315         this.hide();
24316     },
24317     
24318     show : function()
24319     {
24320         this.picker().show();
24321         this.pop.show();
24322         this.update();
24323         this.place();
24324         
24325         this.fireEvent('show', this, this.date);
24326     },
24327     
24328     hide : function()
24329     {
24330         this.picker().hide();
24331         this.pop.hide();
24332         
24333         this.fireEvent('hide', this, this.date);
24334     },
24335     
24336     setTime : function()
24337     {
24338         this.hide();
24339         this.setValue(this.time.format(this.format));
24340         
24341         this.fireEvent('select', this, this.date);
24342         
24343         
24344     },
24345     
24346     onMousedown: function(e){
24347         e.stopPropagation();
24348         e.preventDefault();
24349     },
24350     
24351     onIncrementHours: function()
24352     {
24353         Roo.log('onIncrementHours');
24354         this.time = this.time.add(Date.HOUR, 1);
24355         this.update();
24356         
24357     },
24358     
24359     onDecrementHours: function()
24360     {
24361         Roo.log('onDecrementHours');
24362         this.time = this.time.add(Date.HOUR, -1);
24363         this.update();
24364     },
24365     
24366     onIncrementMinutes: function()
24367     {
24368         Roo.log('onIncrementMinutes');
24369         var minutesToAdd = Math.round((parseInt(this.time.format('i')) + this.minuteStep) / this.minuteStep) * this.minuteStep - parseInt(this.time.format('i'));
24370         this.time = this.time.add(Date.MINUTE, minutesToAdd);
24371         this.update();
24372     },
24373     
24374     onDecrementMinutes: function()
24375     {
24376         Roo.log('onDecrementMinutes');
24377         var minutesToSubtract = parseInt(this.time.format('i')) - Math.round((parseInt(this.time.format('i')) - this.minuteStep) / this.minuteStep) * this.minuteStep;
24378         this.time = this.time.add(Date.MINUTE, -1 * minutesToSubtract);
24379         this.update();
24380     },
24381     
24382     onTogglePeriod: function()
24383     {
24384         Roo.log('onTogglePeriod');
24385         this.time = this.time.add(Date.HOUR, 12);
24386         this.update();
24387     }
24388     
24389    
24390 });
24391  
24392
24393 Roo.apply(Roo.bootstrap.form.TimeField,  {
24394   
24395     template : {
24396         tag: 'div',
24397         cls: 'datepicker dropdown-menu',
24398         cn: [
24399             {
24400                 tag: 'div',
24401                 cls: 'datepicker-time',
24402                 cn: [
24403                 {
24404                     tag: 'table',
24405                     cls: 'table-condensed',
24406                     cn:[
24407                         {
24408                             tag: 'tbody',
24409                             cn: [
24410                                 {
24411                                     tag: 'tr',
24412                                     cn: [
24413                                     {
24414                                         tag: 'td',
24415                                         colspan: '7'
24416                                     }
24417                                     ]
24418                                 }
24419                             ]
24420                         },
24421                         {
24422                             tag: 'tfoot',
24423                             cn: [
24424                                 {
24425                                     tag: 'tr',
24426                                     cn: [
24427                                     {
24428                                         tag: 'th',
24429                                         colspan: '7',
24430                                         cls: '',
24431                                         cn: [
24432                                             {
24433                                                 tag: 'button',
24434                                                 cls: 'btn btn-info ok',
24435                                                 html: 'OK'
24436                                             }
24437                                         ]
24438                                     }
24439                     
24440                                     ]
24441                                 }
24442                             ]
24443                         }
24444                     ]
24445                 }
24446                 ]
24447             }
24448         ]
24449     }
24450 });
24451
24452  
24453
24454  /*
24455  * - LGPL
24456  *
24457  * MonthField
24458  * 
24459  */
24460
24461 /**
24462  * @class Roo.bootstrap.form.MonthField
24463  * @extends Roo.bootstrap.form.Input
24464  * Bootstrap MonthField class
24465  * 
24466  * @cfg {String} language default en
24467  * 
24468  * @constructor
24469  * Create a new MonthField
24470  * @param {Object} config The config object
24471  */
24472
24473 Roo.bootstrap.form.MonthField = function(config){
24474     Roo.bootstrap.form.MonthField.superclass.constructor.call(this, config);
24475     
24476     this.addEvents({
24477         /**
24478          * @event show
24479          * Fires when this field show.
24480          * @param {Roo.bootstrap.form.MonthField} this
24481          * @param {Mixed} date The date value
24482          */
24483         show : true,
24484         /**
24485          * @event show
24486          * Fires when this field hide.
24487          * @param {Roo.bootstrap.form.MonthField} this
24488          * @param {Mixed} date The date value
24489          */
24490         hide : true,
24491         /**
24492          * @event select
24493          * Fires when select a date.
24494          * @param {Roo.bootstrap.form.MonthField} this
24495          * @param {String} oldvalue The old value
24496          * @param {String} newvalue The new value
24497          */
24498         select : true
24499     });
24500 };
24501
24502 Roo.extend(Roo.bootstrap.form.MonthField, Roo.bootstrap.form.Input,  {
24503     
24504     onRender: function(ct, position)
24505     {
24506         
24507         Roo.bootstrap.form.MonthField.superclass.onRender.call(this, ct, position);
24508         
24509         this.language = this.language || 'en';
24510         this.language = this.language in Roo.bootstrap.form.MonthField.dates ? this.language : this.language.split('-')[0];
24511         this.language = this.language in Roo.bootstrap.form.MonthField.dates ? this.language : "en";
24512         
24513         this.isRTL = Roo.bootstrap.form.MonthField.dates[this.language].rtl || false;
24514         this.isInline = false;
24515         this.isInput = true;
24516         this.component = this.el.select('.add-on', true).first() || false;
24517         this.component = (this.component && this.component.length === 0) ? false : this.component;
24518         this.hasInput = this.component && this.inputEL().length;
24519         
24520         this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.form.MonthField.template);
24521         
24522         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
24523         
24524         this.picker().on('mousedown', this.onMousedown, this);
24525         this.picker().on('click', this.onClick, this);
24526         
24527         this.picker().addClass('datepicker-dropdown');
24528         
24529         Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
24530             v.setStyle('width', '189px');
24531         });
24532         
24533         this.fillMonths();
24534         
24535         this.update();
24536         
24537         if(this.isInline) {
24538             this.show();
24539         }
24540         
24541     },
24542     
24543     setValue: function(v, suppressEvent)
24544     {   
24545         var o = this.getValue();
24546         
24547         Roo.bootstrap.form.MonthField.superclass.setValue.call(this, v);
24548         
24549         this.update();
24550
24551         if(suppressEvent !== true){
24552             this.fireEvent('select', this, o, v);
24553         }
24554         
24555     },
24556     
24557     getValue: function()
24558     {
24559         return this.value;
24560     },
24561     
24562     onClick: function(e) 
24563     {
24564         e.stopPropagation();
24565         e.preventDefault();
24566         
24567         var target = e.getTarget();
24568         
24569         if(target.nodeName.toLowerCase() === 'i'){
24570             target = Roo.get(target).dom.parentNode;
24571         }
24572         
24573         var nodeName = target.nodeName;
24574         var className = target.className;
24575         var html = target.innerHTML;
24576         
24577         if(nodeName.toLowerCase() != 'span' || className.indexOf('disabled') > -1 || className.indexOf('month') == -1){
24578             return;
24579         }
24580         
24581         this.vIndex = Roo.bootstrap.form.MonthField.dates[this.language].monthsShort.indexOf(html);
24582         
24583         this.setValue(Roo.bootstrap.form.MonthField.dates[this.language].months[this.vIndex]);
24584         
24585         this.hide();
24586                         
24587     },
24588     
24589     picker : function()
24590     {
24591         return this.pickerEl;
24592     },
24593     
24594     fillMonths: function()
24595     {    
24596         var i = 0;
24597         var months = this.picker().select('>.datepicker-months td', true).first();
24598         
24599         months.dom.innerHTML = '';
24600         
24601         while (i < 12) {
24602             var month = {
24603                 tag: 'span',
24604                 cls: 'month',
24605                 html: Roo.bootstrap.form.MonthField.dates[this.language].monthsShort[i++]
24606             };
24607             
24608             months.createChild(month);
24609         }
24610         
24611     },
24612     
24613     update: function()
24614     {
24615         var _this = this;
24616         
24617         if(typeof(this.vIndex) == 'undefined' && this.value.length){
24618             this.vIndex = Roo.bootstrap.form.MonthField.dates[this.language].months.indexOf(this.value);
24619         }
24620         
24621         Roo.each(this.pickerEl.select('> .datepicker-months tbody > tr > td > span', true).elements, function(e, k){
24622             e.removeClass('active');
24623             
24624             if(typeof(_this.vIndex) != 'undefined' && k == _this.vIndex){
24625                 e.addClass('active');
24626             }
24627         })
24628     },
24629     
24630     place: function()
24631     {
24632         if(this.isInline) {
24633             return;
24634         }
24635         
24636         this.picker().removeClass(['bottom', 'top']);
24637         
24638         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
24639             /*
24640              * place to the top of element!
24641              *
24642              */
24643             
24644             this.picker().addClass('top');
24645             this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
24646             
24647             return;
24648         }
24649         
24650         this.picker().addClass('bottom');
24651         
24652         this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
24653     },
24654     
24655     onFocus : function()
24656     {
24657         Roo.bootstrap.form.MonthField.superclass.onFocus.call(this);
24658         this.show();
24659     },
24660     
24661     onBlur : function()
24662     {
24663         Roo.bootstrap.form.MonthField.superclass.onBlur.call(this);
24664         
24665         var d = this.inputEl().getValue();
24666         
24667         this.setValue(d);
24668                 
24669         this.hide();
24670     },
24671     
24672     show : function()
24673     {
24674         this.picker().show();
24675         this.picker().select('>.datepicker-months', true).first().show();
24676         this.update();
24677         this.place();
24678         
24679         this.fireEvent('show', this, this.date);
24680     },
24681     
24682     hide : function()
24683     {
24684         if(this.isInline) {
24685             return;
24686         }
24687         this.picker().hide();
24688         this.fireEvent('hide', this, this.date);
24689         
24690     },
24691     
24692     onMousedown: function(e)
24693     {
24694         e.stopPropagation();
24695         e.preventDefault();
24696     },
24697     
24698     keyup: function(e)
24699     {
24700         Roo.bootstrap.form.MonthField.superclass.keyup.call(this);
24701         this.update();
24702     },
24703
24704     fireKey: function(e)
24705     {
24706         if (!this.picker().isVisible()){
24707             if (e.keyCode == 27)   {// allow escape to hide and re-show picker
24708                 this.show();
24709             }
24710             return;
24711         }
24712         
24713         var dir;
24714         
24715         switch(e.keyCode){
24716             case 27: // escape
24717                 this.hide();
24718                 e.preventDefault();
24719                 break;
24720             case 37: // left
24721             case 39: // right
24722                 dir = e.keyCode == 37 ? -1 : 1;
24723                 
24724                 this.vIndex = this.vIndex + dir;
24725                 
24726                 if(this.vIndex < 0){
24727                     this.vIndex = 0;
24728                 }
24729                 
24730                 if(this.vIndex > 11){
24731                     this.vIndex = 11;
24732                 }
24733                 
24734                 if(isNaN(this.vIndex)){
24735                     this.vIndex = 0;
24736                 }
24737                 
24738                 this.setValue(Roo.bootstrap.form.MonthField.dates[this.language].months[this.vIndex]);
24739                 
24740                 break;
24741             case 38: // up
24742             case 40: // down
24743                 
24744                 dir = e.keyCode == 38 ? -1 : 1;
24745                 
24746                 this.vIndex = this.vIndex + dir * 4;
24747                 
24748                 if(this.vIndex < 0){
24749                     this.vIndex = 0;
24750                 }
24751                 
24752                 if(this.vIndex > 11){
24753                     this.vIndex = 11;
24754                 }
24755                 
24756                 if(isNaN(this.vIndex)){
24757                     this.vIndex = 0;
24758                 }
24759                 
24760                 this.setValue(Roo.bootstrap.form.MonthField.dates[this.language].months[this.vIndex]);
24761                 break;
24762                 
24763             case 13: // enter
24764                 
24765                 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
24766                     this.setValue(Roo.bootstrap.form.MonthField.dates[this.language].months[this.vIndex]);
24767                 }
24768                 
24769                 this.hide();
24770                 e.preventDefault();
24771                 break;
24772             case 9: // tab
24773                 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
24774                     this.setValue(Roo.bootstrap.form.MonthField.dates[this.language].months[this.vIndex]);
24775                 }
24776                 this.hide();
24777                 break;
24778             case 16: // shift
24779             case 17: // ctrl
24780             case 18: // alt
24781                 break;
24782             default :
24783                 this.hide();
24784                 
24785         }
24786     },
24787     
24788     remove: function() 
24789     {
24790         this.picker().remove();
24791     }
24792    
24793 });
24794
24795 Roo.apply(Roo.bootstrap.form.MonthField,  {
24796     
24797     content : {
24798         tag: 'tbody',
24799         cn: [
24800         {
24801             tag: 'tr',
24802             cn: [
24803             {
24804                 tag: 'td',
24805                 colspan: '7'
24806             }
24807             ]
24808         }
24809         ]
24810     },
24811     
24812     dates:{
24813         en: {
24814             months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
24815             monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"]
24816         }
24817     }
24818 });
24819
24820 Roo.apply(Roo.bootstrap.form.MonthField,  {
24821   
24822     template : {
24823         tag: 'div',
24824         cls: 'datepicker dropdown-menu roo-dynamic',
24825         cn: [
24826             {
24827                 tag: 'div',
24828                 cls: 'datepicker-months',
24829                 cn: [
24830                 {
24831                     tag: 'table',
24832                     cls: 'table-condensed',
24833                     cn:[
24834                         Roo.bootstrap.form.DateField.content
24835                     ]
24836                 }
24837                 ]
24838             }
24839         ]
24840     }
24841 });
24842
24843  
24844
24845  
24846  /*
24847  * - LGPL
24848  *
24849  * CheckBox
24850  * 
24851  */
24852
24853 /**
24854  * @class Roo.bootstrap.form.CheckBox
24855  * @extends Roo.bootstrap.form.Input
24856  * Bootstrap CheckBox class
24857  * 
24858  * @cfg {String} valueOff The value that should go into the generated input element's value when unchecked.
24859  * @cfg {String} inputValue The value that should go into the generated input element's value when checked.
24860  * @cfg {String} boxLabel The text that appears beside the checkbox
24861  * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the checkbox
24862  * @cfg {Boolean} checked initnal the element
24863  * @cfg {Boolean} inline inline the element (default false)
24864  * @cfg {String} groupId the checkbox group id // normal just use for checkbox
24865  * @cfg {String} tooltip label tooltip
24866  * 
24867  * @constructor
24868  * Create a new CheckBox
24869  * @param {Object} config The config object
24870  */
24871
24872 Roo.bootstrap.form.CheckBox = function(config){
24873     Roo.bootstrap.form.CheckBox.superclass.constructor.call(this, config);
24874    
24875     this.addEvents({
24876         /**
24877         * @event check
24878         * Fires when the element is checked or unchecked.
24879         * @param {Roo.bootstrap.form.CheckBox} this This input
24880         * @param {Boolean} checked The new checked value
24881         */
24882        check : true,
24883        /**
24884         * @event click
24885         * Fires when the element is click.
24886         * @param {Roo.bootstrap.form.CheckBox} this This input
24887         */
24888        click : true
24889     });
24890     
24891 };
24892
24893 Roo.extend(Roo.bootstrap.form.CheckBox, Roo.bootstrap.form.Input,  {
24894   
24895     inputType: 'checkbox',
24896     inputValue: 1,
24897     valueOff: 0,
24898     boxLabel: false,
24899     checked: false,
24900     weight : false,
24901     inline: false,
24902     tooltip : '',
24903     
24904     // checkbox success does not make any sense really.. 
24905     invalidClass : "",
24906     validClass : "",
24907     
24908     
24909     getAutoCreate : function()
24910     {
24911         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
24912         
24913         var id = Roo.id();
24914         
24915         var cfg = {};
24916         
24917         cfg.cls = 'form-group form-check ' + this.inputType; //input-group
24918         
24919         if(this.inline){
24920             cfg.cls += ' ' + this.inputType + '-inline  form-check-inline';
24921         }
24922         
24923         var input =  {
24924             tag: 'input',
24925             id : id,
24926             type : this.inputType,
24927             value : this.inputValue,
24928             cls : 'roo-' + this.inputType, //'form-box',
24929             placeholder : this.placeholder || ''
24930             
24931         };
24932         
24933         if(this.inputType != 'radio'){
24934             var hidden =  {
24935                 tag: 'input',
24936                 type : 'hidden',
24937                 cls : 'roo-hidden-value',
24938                 value : this.checked ? this.inputValue : this.valueOff
24939             };
24940         }
24941         
24942             
24943         if (this.weight) { // Validity check?
24944             cfg.cls += " " + this.inputType + "-" + this.weight;
24945         }
24946         
24947         if (this.disabled) {
24948             input.disabled=true;
24949         }
24950         
24951         if(this.checked){
24952             input.checked = this.checked;
24953         }
24954         
24955         if (this.name) {
24956             
24957             input.name = this.name;
24958             
24959             if(this.inputType != 'radio'){
24960                 hidden.name = this.name;
24961                 input.name = '_hidden_' + this.name;
24962             }
24963         }
24964         
24965         if (this.size) {
24966             input.cls += ' input-' + this.size;
24967         }
24968         
24969         var settings=this;
24970         
24971         ['xs','sm','md','lg'].map(function(size){
24972             if (settings[size]) {
24973                 cfg.cls += ' col-' + size + '-' + settings[size];
24974             }
24975         });
24976         
24977         var inputblock = input;
24978          
24979         if (this.before || this.after) {
24980             
24981             inputblock = {
24982                 cls : 'input-group',
24983                 cn :  [] 
24984             };
24985             
24986             if (this.before) {
24987                 inputblock.cn.push({
24988                     tag :'span',
24989                     cls : 'input-group-addon',
24990                     html : this.before
24991                 });
24992             }
24993             
24994             inputblock.cn.push(input);
24995             
24996             if(this.inputType != 'radio'){
24997                 inputblock.cn.push(hidden);
24998             }
24999             
25000             if (this.after) {
25001                 inputblock.cn.push({
25002                     tag :'span',
25003                     cls : 'input-group-addon',
25004                     html : this.after
25005                 });
25006             }
25007             
25008         }
25009         var boxLabelCfg = false;
25010         
25011         if(this.boxLabel){
25012            
25013             boxLabelCfg = {
25014                 tag: 'label',
25015                 //'for': id, // box label is handled by onclick - so no for...
25016                 cls: 'box-label',
25017                 html: this.boxLabel
25018             };
25019             if(this.tooltip){
25020                 boxLabelCfg.tooltip = this.tooltip;
25021             }
25022              
25023         }
25024         
25025         
25026         if (align ==='left' && this.fieldLabel.length) {
25027 //                Roo.log("left and has label");
25028             cfg.cn = [
25029                 {
25030                     tag: 'label',
25031                     'for' :  id,
25032                     cls : 'control-label',
25033                     html : this.fieldLabel
25034                 },
25035                 {
25036                     cls : "", 
25037                     cn: [
25038                         inputblock
25039                     ]
25040                 }
25041             ];
25042             
25043             if (boxLabelCfg) {
25044                 cfg.cn[1].cn.push(boxLabelCfg);
25045             }
25046             
25047             if(this.labelWidth > 12){
25048                 cfg.cn[0].style = "width: " + this.labelWidth + 'px';
25049             }
25050             
25051             if(this.labelWidth < 13 && this.labelmd == 0){
25052                 this.labelmd = this.labelWidth;
25053             }
25054             
25055             if(this.labellg > 0){
25056                 cfg.cn[0].cls += ' col-lg-' + this.labellg;
25057                 cfg.cn[1].cls += ' col-lg-' + (12 - this.labellg);
25058             }
25059             
25060             if(this.labelmd > 0){
25061                 cfg.cn[0].cls += ' col-md-' + this.labelmd;
25062                 cfg.cn[1].cls += ' col-md-' + (12 - this.labelmd);
25063             }
25064             
25065             if(this.labelsm > 0){
25066                 cfg.cn[0].cls += ' col-sm-' + this.labelsm;
25067                 cfg.cn[1].cls += ' col-sm-' + (12 - this.labelsm);
25068             }
25069             
25070             if(this.labelxs > 0){
25071                 cfg.cn[0].cls += ' col-xs-' + this.labelxs;
25072                 cfg.cn[1].cls += ' col-xs-' + (12 - this.labelxs);
25073             }
25074             
25075         } else if ( this.fieldLabel.length) {
25076 //                Roo.log(" label");
25077                 cfg.cn = [
25078                    
25079                     {
25080                         tag: this.boxLabel ? 'span' : 'label',
25081                         'for': id,
25082                         cls: 'control-label box-input-label',
25083                         //cls : 'input-group-addon',
25084                         html : this.fieldLabel
25085                     },
25086                     
25087                     inputblock
25088                     
25089                 ];
25090                 if (boxLabelCfg) {
25091                     cfg.cn.push(boxLabelCfg);
25092                 }
25093
25094         } else {
25095             
25096 //                Roo.log(" no label && no align");
25097                 cfg.cn = [  inputblock ] ;
25098                 if (boxLabelCfg) {
25099                     cfg.cn.push(boxLabelCfg);
25100                 }
25101
25102                 
25103         }
25104         
25105        
25106         
25107         if(this.inputType != 'radio'){
25108             cfg.cn.push(hidden);
25109         }
25110         
25111         return cfg;
25112         
25113     },
25114     
25115     /**
25116      * return the real input element.
25117      */
25118     inputEl: function ()
25119     {
25120         return this.el.select('input.roo-' + this.inputType,true).first();
25121     },
25122     hiddenEl: function ()
25123     {
25124         return this.el.select('input.roo-hidden-value',true).first();
25125     },
25126     
25127     labelEl: function()
25128     {
25129         return this.el.select('label.control-label',true).first();
25130     },
25131     /* depricated... */
25132     
25133     label: function()
25134     {
25135         return this.labelEl();
25136     },
25137     
25138     boxLabelEl: function()
25139     {
25140         return this.el.select('label.box-label',true).first();
25141     },
25142     
25143     initEvents : function()
25144     {
25145 //        Roo.bootstrap.form.CheckBox.superclass.initEvents.call(this);
25146         
25147         this.inputEl().on('click', this.onClick,  this);
25148         
25149         if (this.boxLabel) { 
25150             this.el.select('label.box-label',true).first().on('click', this.onClick,  this);
25151         }
25152         
25153         this.startValue = this.getValue();
25154         
25155         if(this.groupId){
25156             Roo.bootstrap.form.CheckBox.register(this);
25157         }
25158     },
25159     
25160     onClick : function(e)
25161     {   
25162         if(this.fireEvent('click', this, e) !== false){
25163             this.setChecked(!this.checked);
25164         }
25165         
25166     },
25167     
25168     setChecked : function(state,suppressEvent)
25169     {
25170         this.startValue = this.getValue();
25171
25172         if(this.inputType == 'radio'){
25173             
25174             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
25175                 e.dom.checked = false;
25176             });
25177             
25178             this.inputEl().dom.checked = true;
25179             
25180             this.inputEl().dom.value = this.inputValue;
25181             
25182             if(suppressEvent !== true){
25183                 this.fireEvent('check', this, true);
25184             }
25185             
25186             this.validate();
25187             
25188             return;
25189         }
25190         
25191         this.checked = state;
25192         
25193         this.inputEl().dom.checked = state;
25194         
25195         
25196         this.hiddenEl().dom.value = state ? this.inputValue : this.valueOff;
25197         
25198         if(suppressEvent !== true){
25199             this.fireEvent('check', this, state);
25200         }
25201         
25202         this.validate();
25203     },
25204     
25205     getValue : function()
25206     {
25207         if(this.inputType == 'radio'){
25208             return this.getGroupValue();
25209         }
25210         
25211         return this.hiddenEl().dom.value;
25212         
25213     },
25214     
25215     getGroupValue : function()
25216     {
25217         if(typeof(this.el.up('form').child('input[name='+this.name+']:checked', true)) == 'undefined'){
25218             return '';
25219         }
25220         
25221         return this.el.up('form').child('input[name='+this.name+']:checked', true).value;
25222     },
25223     
25224     setValue : function(v,suppressEvent)
25225     {
25226         if(this.inputType == 'radio'){
25227             this.setGroupValue(v, suppressEvent);
25228             return;
25229         }
25230         
25231         this.setChecked(((typeof(v) == 'undefined') ? this.checked : (String(v) === String(this.inputValue))), suppressEvent);
25232         
25233         this.validate();
25234     },
25235     
25236     setGroupValue : function(v, suppressEvent)
25237     {
25238         this.startValue = this.getValue();
25239         
25240         Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
25241             e.dom.checked = false;
25242             
25243             if(e.dom.value == v){
25244                 e.dom.checked = true;
25245             }
25246         });
25247         
25248         if(suppressEvent !== true){
25249             this.fireEvent('check', this, true);
25250         }
25251
25252         this.validate();
25253         
25254         return;
25255     },
25256     
25257     validate : function()
25258     {
25259         if(this.getVisibilityEl().hasClass('hidden')){
25260             return true;
25261         }
25262         
25263         if(
25264                 this.disabled || 
25265                 (this.inputType == 'radio' && this.validateRadio()) ||
25266                 (this.inputType == 'checkbox' && this.validateCheckbox())
25267         ){
25268             this.markValid();
25269             return true;
25270         }
25271         
25272         this.markInvalid();
25273         return false;
25274     },
25275     
25276     validateRadio : function()
25277     {
25278         if(this.getVisibilityEl().hasClass('hidden')){
25279             return true;
25280         }
25281         
25282         if(this.allowBlank){
25283             return true;
25284         }
25285         
25286         var valid = false;
25287         
25288         Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
25289             if(!e.dom.checked){
25290                 return;
25291             }
25292             
25293             valid = true;
25294             
25295             return false;
25296         });
25297         
25298         return valid;
25299     },
25300     
25301     validateCheckbox : function()
25302     {
25303         if(!this.groupId){
25304             return (this.getValue() == this.inputValue || this.allowBlank) ? true : false;
25305             //return (this.getValue() == this.inputValue) ? true : false;
25306         }
25307         
25308         var group = Roo.bootstrap.form.CheckBox.get(this.groupId);
25309         
25310         if(!group){
25311             return false;
25312         }
25313         
25314         var r = false;
25315         
25316         for(var i in group){
25317             if(group[i].el.isVisible(true)){
25318                 r = false;
25319                 break;
25320             }
25321             
25322             r = true;
25323         }
25324         
25325         for(var i in group){
25326             if(r){
25327                 break;
25328             }
25329             
25330             r = (group[i].getValue() == group[i].inputValue) ? true : false;
25331         }
25332         
25333         return r;
25334     },
25335     
25336     /**
25337      * Mark this field as valid
25338      */
25339     markValid : function()
25340     {
25341         var _this = this;
25342         
25343         this.fireEvent('valid', this);
25344         
25345         var label = Roo.bootstrap.form.FieldLabel.get(this.name + '-group');
25346         
25347         if(this.groupId){
25348             label = Roo.bootstrap.form.FieldLabel.get(this.groupId + '-group');
25349         }
25350         
25351         if(label){
25352             label.markValid();
25353         }
25354
25355         if(this.inputType == 'radio'){
25356             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
25357                 var fg = e.findParent('.form-group', false, true);
25358                 if (Roo.bootstrap.version == 3) {
25359                     fg.removeClass([_this.invalidClass, _this.validClass]);
25360                     fg.addClass(_this.validClass);
25361                 } else {
25362                     fg.removeClass(['is-valid', 'is-invalid']);
25363                     fg.addClass('is-valid');
25364                 }
25365             });
25366             
25367             return;
25368         }
25369
25370         if(!this.groupId){
25371             var fg = this.el.findParent('.form-group', false, true);
25372             if (Roo.bootstrap.version == 3) {
25373                 fg.removeClass([this.invalidClass, this.validClass]);
25374                 fg.addClass(this.validClass);
25375             } else {
25376                 fg.removeClass(['is-valid', 'is-invalid']);
25377                 fg.addClass('is-valid');
25378             }
25379             return;
25380         }
25381         
25382         var group = Roo.bootstrap.form.CheckBox.get(this.groupId);
25383         
25384         if(!group){
25385             return;
25386         }
25387         
25388         for(var i in group){
25389             var fg = group[i].el.findParent('.form-group', false, true);
25390             if (Roo.bootstrap.version == 3) {
25391                 fg.removeClass([this.invalidClass, this.validClass]);
25392                 fg.addClass(this.validClass);
25393             } else {
25394                 fg.removeClass(['is-valid', 'is-invalid']);
25395                 fg.addClass('is-valid');
25396             }
25397         }
25398     },
25399     
25400      /**
25401      * Mark this field as invalid
25402      * @param {String} msg The validation message
25403      */
25404     markInvalid : function(msg)
25405     {
25406         if(this.allowBlank){
25407             return;
25408         }
25409         
25410         var _this = this;
25411         
25412         this.fireEvent('invalid', this, msg);
25413         
25414         var label = Roo.bootstrap.form.FieldLabel.get(this.name + '-group');
25415         
25416         if(this.groupId){
25417             label = Roo.bootstrap.form.FieldLabel.get(this.groupId + '-group');
25418         }
25419         
25420         if(label){
25421             label.markInvalid();
25422         }
25423             
25424         if(this.inputType == 'radio'){
25425             
25426             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
25427                 var fg = e.findParent('.form-group', false, true);
25428                 if (Roo.bootstrap.version == 3) {
25429                     fg.removeClass([_this.invalidClass, _this.validClass]);
25430                     fg.addClass(_this.invalidClass);
25431                 } else {
25432                     fg.removeClass(['is-invalid', 'is-valid']);
25433                     fg.addClass('is-invalid');
25434                 }
25435             });
25436             
25437             return;
25438         }
25439         
25440         if(!this.groupId){
25441             var fg = this.el.findParent('.form-group', false, true);
25442             if (Roo.bootstrap.version == 3) {
25443                 fg.removeClass([_this.invalidClass, _this.validClass]);
25444                 fg.addClass(_this.invalidClass);
25445             } else {
25446                 fg.removeClass(['is-invalid', 'is-valid']);
25447                 fg.addClass('is-invalid');
25448             }
25449             return;
25450         }
25451         
25452         var group = Roo.bootstrap.form.CheckBox.get(this.groupId);
25453         
25454         if(!group){
25455             return;
25456         }
25457         
25458         for(var i in group){
25459             var fg = group[i].el.findParent('.form-group', false, true);
25460             if (Roo.bootstrap.version == 3) {
25461                 fg.removeClass([_this.invalidClass, _this.validClass]);
25462                 fg.addClass(_this.invalidClass);
25463             } else {
25464                 fg.removeClass(['is-invalid', 'is-valid']);
25465                 fg.addClass('is-invalid');
25466             }
25467         }
25468         
25469     },
25470     
25471     clearInvalid : function()
25472     {
25473         Roo.bootstrap.form.Input.prototype.clearInvalid.call(this);
25474         
25475         // this.el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
25476         
25477         var label = Roo.bootstrap.form.FieldLabel.get(this.name + '-group');
25478         
25479         if (label && label.iconEl) {
25480             label.iconEl.removeClass([ label.validClass, label.invalidClass ]);
25481             label.iconEl.removeClass(['is-invalid', 'is-valid']);
25482         }
25483     },
25484     
25485     disable : function()
25486     {
25487         if(this.inputType != 'radio'){
25488             Roo.bootstrap.form.CheckBox.superclass.disable.call(this);
25489             return;
25490         }
25491         
25492         var _this = this;
25493         
25494         if(this.rendered){
25495             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
25496                 _this.getActionEl().addClass(this.disabledClass);
25497                 e.dom.disabled = true;
25498             });
25499         }
25500         
25501         this.disabled = true;
25502         this.fireEvent("disable", this);
25503         return this;
25504     },
25505
25506     enable : function()
25507     {
25508         if(this.inputType != 'radio'){
25509             Roo.bootstrap.form.CheckBox.superclass.enable.call(this);
25510             return;
25511         }
25512         
25513         var _this = this;
25514         
25515         if(this.rendered){
25516             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
25517                 _this.getActionEl().removeClass(this.disabledClass);
25518                 e.dom.disabled = false;
25519             });
25520         }
25521         
25522         this.disabled = false;
25523         this.fireEvent("enable", this);
25524         return this;
25525     },
25526     
25527     setBoxLabel : function(v)
25528     {
25529         this.boxLabel = v;
25530         
25531         if(this.rendered){
25532             this.el.select('label.box-label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
25533         }
25534     }
25535
25536 });
25537
25538 Roo.apply(Roo.bootstrap.form.CheckBox, {
25539     
25540     groups: {},
25541     
25542      /**
25543     * register a CheckBox Group
25544     * @param {Roo.bootstrap.form.CheckBox} the CheckBox to add
25545     */
25546     register : function(checkbox)
25547     {
25548         if(typeof(this.groups[checkbox.groupId]) == 'undefined'){
25549             this.groups[checkbox.groupId] = {};
25550         }
25551         
25552         if(this.groups[checkbox.groupId].hasOwnProperty(checkbox.name)){
25553             return;
25554         }
25555         
25556         this.groups[checkbox.groupId][checkbox.name] = checkbox;
25557         
25558     },
25559     /**
25560     * fetch a CheckBox Group based on the group ID
25561     * @param {string} the group ID
25562     * @returns {Roo.bootstrap.form.CheckBox} the CheckBox group
25563     */
25564     get: function(groupId) {
25565         if (typeof(this.groups[groupId]) == 'undefined') {
25566             return false;
25567         }
25568         
25569         return this.groups[groupId] ;
25570     }
25571     
25572     
25573 });
25574 /*
25575  * - LGPL
25576  *
25577  * RadioItem
25578  * 
25579  */
25580
25581 /**
25582  * @class Roo.bootstrap.form.Radio
25583  * @extends Roo.bootstrap.Component
25584  * Bootstrap Radio class
25585  * @cfg {String} boxLabel - the label associated
25586  * @cfg {String} value - the value of radio
25587  * 
25588  * @constructor
25589  * Create a new Radio
25590  * @param {Object} config The config object
25591  */
25592 Roo.bootstrap.form.Radio = function(config){
25593     Roo.bootstrap.form.Radio.superclass.constructor.call(this, config);
25594     
25595 };
25596
25597 Roo.extend(Roo.bootstrap.form.Radio, Roo.bootstrap.Component, {
25598     
25599     boxLabel : '',
25600     
25601     value : '',
25602     
25603     getAutoCreate : function()
25604     {
25605         var cfg = {
25606             tag : 'div',
25607             cls : 'form-group radio',
25608             cn : [
25609                 {
25610                     tag : 'label',
25611                     cls : 'box-label',
25612                     html : this.boxLabel
25613                 }
25614             ]
25615         };
25616         
25617         return cfg;
25618     },
25619     
25620     initEvents : function() 
25621     {
25622         this.parent().register(this);
25623         
25624         this.el.on('click', this.onClick, this);
25625         
25626     },
25627     
25628     onClick : function(e)
25629     {
25630         if(this.parent().fireEvent('click', this.parent(), this, e) !== false){
25631             this.setChecked(true);
25632         }
25633     },
25634     
25635     setChecked : function(state, suppressEvent)
25636     {
25637         this.parent().setValue(this.value, suppressEvent);
25638         
25639     },
25640     
25641     setBoxLabel : function(v)
25642     {
25643         this.boxLabel = v;
25644         
25645         if(this.rendered){
25646             this.el.select('label.box-label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
25647         }
25648     }
25649     
25650 });
25651  
25652
25653  /*
25654  * - LGPL
25655  *
25656  * Input
25657  * 
25658  */
25659
25660 /**
25661  * @class Roo.bootstrap.form.SecurePass
25662  * @extends Roo.bootstrap.form.Input
25663  * Bootstrap SecurePass class
25664  *
25665  * 
25666  * @constructor
25667  * Create a new SecurePass
25668  * @param {Object} config The config object
25669  */
25670  
25671 Roo.bootstrap.form.SecurePass = function (config) {
25672     // these go here, so the translation tool can replace them..
25673     this.errors = {
25674         PwdEmpty: "Please type a password, and then retype it to confirm.",
25675         PwdShort: "Your password must be at least 6 characters long. Please type a different password.",
25676         PwdLong: "Your password can't contain more than 16 characters. Please type a different password.",
25677         PwdBadChar: "The password contains characters that aren't allowed. Please type a different password.",
25678         IDInPwd: "Your password can't include the part of your ID. Please type a different password.",
25679         FNInPwd: "Your password can't contain your first name. Please type a different password.",
25680         LNInPwd: "Your password can't contain your last name. Please type a different password.",
25681         TooWeak: "Your password is Too Weak."
25682     },
25683     this.meterLabel = "Password strength:";
25684     this.pwdStrengths = ["Too Weak", "Weak", "Medium", "Strong"];
25685     this.meterClass = [
25686         "roo-password-meter-tooweak", 
25687         "roo-password-meter-weak", 
25688         "roo-password-meter-medium", 
25689         "roo-password-meter-strong", 
25690         "roo-password-meter-grey"
25691     ];
25692     
25693     this.errors = {};
25694     
25695     Roo.bootstrap.form.SecurePass.superclass.constructor.call(this, config);
25696 }
25697
25698 Roo.extend(Roo.bootstrap.form.SecurePass, Roo.bootstrap.form.Input, {
25699     /**
25700      * @cfg {String/Object} errors A Error spec, or true for a default spec (defaults to
25701      * {
25702      *  PwdEmpty: "Please type a password, and then retype it to confirm.",
25703      *  PwdShort: "Your password must be at least 6 characters long. Please type a different password.",
25704      *  PwdLong: "Your password can't contain more than 16 characters. Please type a different password.",
25705      *  PwdBadChar: "The password contains characters that aren't allowed. Please type a different password.",
25706      *  IDInPwd: "Your password can't include the part of your ID. Please type a different password.",
25707      *  FNInPwd: "Your password can't contain your first name. Please type a different password.",
25708      *  LNInPwd: "Your password can't contain your last name. Please type a different password."
25709      * })
25710      */
25711     // private
25712     
25713     meterWidth: 300,
25714     errorMsg :'',    
25715     errors: false,
25716     imageRoot: '/',
25717     /**
25718      * @cfg {String/Object} Label for the strength meter (defaults to
25719      * 'Password strength:')
25720      */
25721     // private
25722     meterLabel: '',
25723     /**
25724      * @cfg {String/Object} pwdStrengths A pwdStrengths spec, or true for a default spec (defaults to
25725      * ['Weak', 'Medium', 'Strong'])
25726      */
25727     // private    
25728     pwdStrengths: false,    
25729     // private
25730     strength: 0,
25731     // private
25732     _lastPwd: null,
25733     // private
25734     kCapitalLetter: 0,
25735     kSmallLetter: 1,
25736     kDigit: 2,
25737     kPunctuation: 3,
25738     
25739     insecure: false,
25740     // private
25741     initEvents: function ()
25742     {
25743         Roo.bootstrap.form.SecurePass.superclass.initEvents.call(this);
25744
25745         if (this.el.is('input[type=password]') && Roo.isSafari) {
25746             this.el.on('keydown', this.SafariOnKeyDown, this);
25747         }
25748
25749         this.el.on('keyup', this.checkStrength, this, {buffer: 50});
25750     },
25751     // private
25752     onRender: function (ct, position)
25753     {
25754         Roo.bootstrap.form.SecurePass.superclass.onRender.call(this, ct, position);
25755         this.wrap = this.el.wrap({cls: 'x-form-field-wrap'});
25756         this.trigger = this.wrap.createChild({tag: 'div', cls: 'StrengthMeter ' + this.triggerClass});
25757
25758         this.trigger.createChild({
25759                    cn: [
25760                     {
25761                     //id: 'PwdMeter',
25762                     tag: 'div',
25763                     cls: 'roo-password-meter-grey col-xs-12',
25764                     style: {
25765                         //width: 0,
25766                         //width: this.meterWidth + 'px'                                                
25767                         }
25768                     },
25769                     {                            
25770                          cls: 'roo-password-meter-text'                          
25771                     }
25772                 ]            
25773         });
25774
25775          
25776         if (this.hideTrigger) {
25777             this.trigger.setDisplayed(false);
25778         }
25779         this.setSize(this.width || '', this.height || '');
25780     },
25781     // private
25782     onDestroy: function ()
25783     {
25784         if (this.trigger) {
25785             this.trigger.removeAllListeners();
25786             this.trigger.remove();
25787         }
25788         if (this.wrap) {
25789             this.wrap.remove();
25790         }
25791         Roo.bootstrap.form.TriggerField.superclass.onDestroy.call(this);
25792     },
25793     // private
25794     checkStrength: function ()
25795     {
25796         var pwd = this.inputEl().getValue();
25797         if (pwd == this._lastPwd) {
25798             return;
25799         }
25800
25801         var strength;
25802         if (this.ClientSideStrongPassword(pwd)) {
25803             strength = 3;
25804         } else if (this.ClientSideMediumPassword(pwd)) {
25805             strength = 2;
25806         } else if (this.ClientSideWeakPassword(pwd)) {
25807             strength = 1;
25808         } else {
25809             strength = 0;
25810         }
25811         
25812         Roo.log('strength1: ' + strength);
25813         
25814         //var pm = this.trigger.child('div/div/div').dom;
25815         var pm = this.trigger.child('div/div');
25816         pm.removeClass(this.meterClass);
25817         pm.addClass(this.meterClass[strength]);
25818                 
25819         
25820         var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;        
25821                 
25822         pt.innerHTML = this.meterLabel + '&nbsp;' + this.pwdStrengths[strength];
25823         
25824         this._lastPwd = pwd;
25825     },
25826     reset: function ()
25827     {
25828         Roo.bootstrap.form.SecurePass.superclass.reset.call(this);
25829         
25830         this._lastPwd = '';
25831         
25832         var pm = this.trigger.child('div/div');
25833         pm.removeClass(this.meterClass);
25834         pm.addClass('roo-password-meter-grey');        
25835         
25836         
25837         var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;        
25838         
25839         pt.innerHTML = '';
25840         this.inputEl().dom.type='password';
25841     },
25842     // private
25843     validateValue: function (value)
25844     {
25845         if (!Roo.bootstrap.form.SecurePass.superclass.validateValue.call(this, value)) {
25846             return false;
25847         }
25848         if (value.length == 0) {
25849             if (this.allowBlank) {
25850                 this.clearInvalid();
25851                 return true;
25852             }
25853
25854             this.markInvalid(this.errors.PwdEmpty);
25855             this.errorMsg = this.errors.PwdEmpty;
25856             return false;
25857         }
25858         
25859         if(this.insecure){
25860             return true;
25861         }
25862         
25863         if (!value.match(/[\x21-\x7e]+/)) {
25864             this.markInvalid(this.errors.PwdBadChar);
25865             this.errorMsg = this.errors.PwdBadChar;
25866             return false;
25867         }
25868         if (value.length < 6) {
25869             this.markInvalid(this.errors.PwdShort);
25870             this.errorMsg = this.errors.PwdShort;
25871             return false;
25872         }
25873         if (value.length > 16) {
25874             this.markInvalid(this.errors.PwdLong);
25875             this.errorMsg = this.errors.PwdLong;
25876             return false;
25877         }
25878         var strength;
25879         if (this.ClientSideStrongPassword(value)) {
25880             strength = 3;
25881         } else if (this.ClientSideMediumPassword(value)) {
25882             strength = 2;
25883         } else if (this.ClientSideWeakPassword(value)) {
25884             strength = 1;
25885         } else {
25886             strength = 0;
25887         }
25888
25889         
25890         if (strength < 2) {
25891             //this.markInvalid(this.errors.TooWeak);
25892             this.errorMsg = this.errors.TooWeak;
25893             //return false;
25894         }
25895         
25896         
25897         console.log('strength2: ' + strength);
25898         
25899         //var pm = this.trigger.child('div/div/div').dom;
25900         
25901         var pm = this.trigger.child('div/div');
25902         pm.removeClass(this.meterClass);
25903         pm.addClass(this.meterClass[strength]);
25904                 
25905         var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;        
25906                 
25907         pt.innerHTML = this.meterLabel + '&nbsp;' + this.pwdStrengths[strength];
25908         
25909         this.errorMsg = ''; 
25910         return true;
25911     },
25912     // private
25913     CharacterSetChecks: function (type)
25914     {
25915         this.type = type;
25916         this.fResult = false;
25917     },
25918     // private
25919     isctype: function (character, type)
25920     {
25921         switch (type) {  
25922             case this.kCapitalLetter:
25923                 if (character >= 'A' && character <= 'Z') {
25924                     return true;
25925                 }
25926                 break;
25927             
25928             case this.kSmallLetter:
25929                 if (character >= 'a' && character <= 'z') {
25930                     return true;
25931                 }
25932                 break;
25933             
25934             case this.kDigit:
25935                 if (character >= '0' && character <= '9') {
25936                     return true;
25937                 }
25938                 break;
25939             
25940             case this.kPunctuation:
25941                 if ('!@#$%^&*()_+-=\'";:[{]}|.>,</?`~'.indexOf(character) >= 0) {
25942                     return true;
25943                 }
25944                 break;
25945             
25946             default:
25947                 return false;
25948         }
25949
25950     },
25951     // private
25952     IsLongEnough: function (pwd, size)
25953     {
25954         return !(pwd == null || isNaN(size) || pwd.length < size);
25955     },
25956     // private
25957     SpansEnoughCharacterSets: function (word, nb)
25958     {
25959         if (!this.IsLongEnough(word, nb))
25960         {
25961             return false;
25962         }
25963
25964         var characterSetChecks = new Array(
25965             new this.CharacterSetChecks(this.kCapitalLetter), new this.CharacterSetChecks(this.kSmallLetter),
25966             new this.CharacterSetChecks(this.kDigit), new this.CharacterSetChecks(this.kPunctuation)
25967         );
25968         
25969         for (var index = 0; index < word.length; ++index) {
25970             for (var nCharSet = 0; nCharSet < characterSetChecks.length; ++nCharSet) {
25971                 if (!characterSetChecks[nCharSet].fResult && this.isctype(word.charAt(index), characterSetChecks[nCharSet].type)) {
25972                     characterSetChecks[nCharSet].fResult = true;
25973                     break;
25974                 }
25975             }
25976         }
25977
25978         var nCharSets = 0;
25979         for (var nCharSet = 0; nCharSet < characterSetChecks.length; ++nCharSet) {
25980             if (characterSetChecks[nCharSet].fResult) {
25981                 ++nCharSets;
25982             }
25983         }
25984
25985         if (nCharSets < nb) {
25986             return false;
25987         }
25988         return true;
25989     },
25990     // private
25991     ClientSideStrongPassword: function (pwd)
25992     {
25993         return this.IsLongEnough(pwd, 8) && this.SpansEnoughCharacterSets(pwd, 3);
25994     },
25995     // private
25996     ClientSideMediumPassword: function (pwd)
25997     {
25998         return this.IsLongEnough(pwd, 7) && this.SpansEnoughCharacterSets(pwd, 2);
25999     },
26000     // private
26001     ClientSideWeakPassword: function (pwd)
26002     {
26003         return this.IsLongEnough(pwd, 6) || !this.IsLongEnough(pwd, 0);
26004     }
26005           
26006 });Roo.rtf = {}; // namespace
26007 Roo.rtf.Hex = function(hex)
26008 {
26009     this.hexstr = hex;
26010 };
26011 Roo.rtf.Paragraph = function(opts)
26012 {
26013     this.content = []; ///??? is that used?
26014 };Roo.rtf.Span = function(opts)
26015 {
26016     this.value = opts.value;
26017 };
26018
26019 Roo.rtf.Group = function(parent)
26020 {
26021     // we dont want to acutally store parent - it will make debug a nightmare..
26022     this.content = [];
26023     this.cn  = [];
26024      
26025        
26026     
26027 };
26028
26029 Roo.rtf.Group.prototype = {
26030     ignorable : false,
26031     content: false,
26032     cn: false,
26033     addContent : function(node) {
26034         // could set styles...
26035         this.content.push(node);
26036     },
26037     addChild : function(cn)
26038     {
26039         this.cn.push(cn);
26040     },
26041     // only for images really...
26042     toDataURL : function()
26043     {
26044         var mimetype = false;
26045         switch(true) {
26046             case this.content.filter(function(a) { return a.value == 'pngblip' } ).length > 0: 
26047                 mimetype = "image/png";
26048                 break;
26049              case this.content.filter(function(a) { return a.value == 'jpegblip' } ).length > 0:
26050                 mimetype = "image/jpeg";
26051                 break;
26052             default :
26053                 return 'about:blank'; // ?? error?
26054         }
26055         
26056         
26057         var hexstring = this.content[this.content.length-1].value;
26058         
26059         return 'data:' + mimetype + ';base64,' + btoa(hexstring.match(/\w{2}/g).map(function(a) {
26060             return String.fromCharCode(parseInt(a, 16));
26061         }).join(""));
26062     }
26063     
26064 };
26065 // this looks like it's normally the {rtf{ .... }}
26066 Roo.rtf.Document = function()
26067 {
26068     // we dont want to acutally store parent - it will make debug a nightmare..
26069     this.rtlch  = [];
26070     this.content = [];
26071     this.cn = [];
26072     
26073 };
26074 Roo.extend(Roo.rtf.Document, Roo.rtf.Group, { 
26075     addChild : function(cn)
26076     {
26077         this.cn.push(cn);
26078         switch(cn.type) {
26079             case 'rtlch': // most content seems to be inside this??
26080             case 'listtext':
26081             case 'shpinst':
26082                 this.rtlch.push(cn);
26083                 return;
26084             default:
26085                 this[cn.type] = cn;
26086         }
26087         
26088     },
26089     
26090     getElementsByType : function(type)
26091     {
26092         var ret =  [];
26093         this._getElementsByType(type, ret, this.cn, 'rtf');
26094         return ret;
26095     },
26096     _getElementsByType : function (type, ret, search_array, path)
26097     {
26098         search_array.forEach(function(n,i) {
26099             if (n.type == type) {
26100                 n.path = path + '/' + n.type + ':' + i;
26101                 ret.push(n);
26102             }
26103             if (n.cn.length > 0) {
26104                 this._getElementsByType(type, ret, n.cn, path + '/' + n.type+':'+i);
26105             }
26106         },this);
26107     }
26108     
26109 });
26110  
26111 Roo.rtf.Ctrl = function(opts)
26112 {
26113     this.value = opts.value;
26114     this.param = opts.param;
26115 };
26116 /**
26117  *
26118  *
26119  * based on this https://github.com/iarna/rtf-parser
26120  * it's really only designed to extract pict from pasted RTF 
26121  *
26122  * usage:
26123  *
26124  *  var images = new Roo.rtf.Parser().parse(a_string).filter(function(g) { return g.type == 'pict'; });
26125  *  
26126  *
26127  */
26128
26129  
26130
26131
26132
26133 Roo.rtf.Parser = function(text) {
26134     //super({objectMode: true})
26135     this.text = '';
26136     this.parserState = this.parseText;
26137     
26138     // these are for interpeter...
26139     this.doc = {};
26140     ///this.parserState = this.parseTop
26141     this.groupStack = [];
26142     this.hexStore = [];
26143     this.doc = false;
26144     
26145     this.groups = []; // where we put the return.
26146     
26147     for (var ii = 0; ii < text.length; ++ii) {
26148         ++this.cpos;
26149         
26150         if (text[ii] === '\n') {
26151             ++this.row;
26152             this.col = 1;
26153         } else {
26154             ++this.col;
26155         }
26156         this.parserState(text[ii]);
26157     }
26158     
26159     
26160     
26161 };
26162 Roo.rtf.Parser.prototype = {
26163     text : '', // string being parsed..
26164     controlWord : '',
26165     controlWordParam :  '',
26166     hexChar : '',
26167     doc : false,
26168     group: false,
26169     groupStack : false,
26170     hexStore : false,
26171     
26172     
26173     cpos : 0, 
26174     row : 1, // reportin?
26175     col : 1, //
26176
26177      
26178     push : function (el)
26179     {
26180         var m = 'cmd'+ el.type;
26181         if (typeof(this[m]) == 'undefined') {
26182             Roo.log('invalid cmd:' + el.type);
26183             return;
26184         }
26185         this[m](el);
26186         //Roo.log(el);
26187     },
26188     flushHexStore : function()
26189     {
26190         if (this.hexStore.length < 1) {
26191             return;
26192         }
26193         var hexstr = this.hexStore.map(
26194             function(cmd) {
26195                 return cmd.value;
26196         }).join('');
26197         
26198         this.group.addContent( new Roo.rtf.Hex( hexstr ));
26199               
26200             
26201         this.hexStore.splice(0)
26202         
26203     },
26204     
26205     cmdgroupstart : function()
26206     {
26207         this.flushHexStore();
26208         if (this.group) {
26209             this.groupStack.push(this.group);
26210         }
26211          // parent..
26212         if (this.doc === false) {
26213             this.group = this.doc = new Roo.rtf.Document();
26214             return;
26215             
26216         }
26217         this.group = new Roo.rtf.Group(this.group);
26218     },
26219     cmdignorable : function()
26220     {
26221         this.flushHexStore();
26222         this.group.ignorable = true;
26223     },
26224     cmdendparagraph : function()
26225     {
26226         this.flushHexStore();
26227         this.group.addContent(new Roo.rtf.Paragraph());
26228     },
26229     cmdgroupend : function ()
26230     {
26231         this.flushHexStore();
26232         var endingGroup = this.group;
26233         
26234         
26235         this.group = this.groupStack.pop();
26236         if (this.group) {
26237             this.group.addChild(endingGroup);
26238         }
26239         
26240         
26241         
26242         var doc = this.group || this.doc;
26243         //if (endingGroup instanceof FontTable) {
26244         //  doc.fonts = endingGroup.table
26245         //} else if (endingGroup instanceof ColorTable) {
26246         //  doc.colors = endingGroup.table
26247         //} else if (endingGroup !== this.doc && !endingGroup.get('ignorable')) {
26248         if (endingGroup.ignorable === false) {
26249             //code
26250             this.groups.push(endingGroup);
26251            // Roo.log( endingGroup );
26252         }
26253             //Roo.each(endingGroup.content, function(item)) {
26254             //    doc.addContent(item);
26255             //}
26256             //process.emit('debug', 'GROUP END', endingGroup.type, endingGroup.get('ignorable'))
26257         //}
26258     },
26259     cmdtext : function (cmd)
26260     {
26261         this.flushHexStore();
26262         if (!this.group) { // an RTF fragment, missing the {\rtf1 header
26263             //this.group = this.doc
26264             return;  // we really don't care about stray text...
26265         }
26266         this.group.addContent(new Roo.rtf.Span(cmd));
26267     },
26268     cmdcontrolword : function (cmd)
26269     {
26270         this.flushHexStore();
26271         if (!this.group.type) {
26272             this.group.type = cmd.value;
26273             return;
26274         }
26275         this.group.addContent(new Roo.rtf.Ctrl(cmd));
26276         // we actually don't care about ctrl words...
26277         return ;
26278         /*
26279         var method = 'ctrl$' + cmd.value.replace(/-(.)/g, (_, char) => char.toUpperCase())
26280         if (this[method]) {
26281             this[method](cmd.param)
26282         } else {
26283             if (!this.group.get('ignorable')) process.emit('debug', method, cmd.param)
26284         }
26285         */
26286     },
26287     cmdhexchar : function(cmd) {
26288         this.hexStore.push(cmd);
26289     },
26290     cmderror : function(cmd) {
26291         throw cmd.value;
26292     },
26293     
26294     /*
26295       _flush (done) {
26296         if (this.text !== '\u0000') this.emitText()
26297         done()
26298       }
26299       */
26300       
26301       
26302     parseText : function(c)
26303     {
26304         if (c === '\\') {
26305             this.parserState = this.parseEscapes;
26306         } else if (c === '{') {
26307             this.emitStartGroup();
26308         } else if (c === '}') {
26309             this.emitEndGroup();
26310         } else if (c === '\x0A' || c === '\x0D') {
26311             // cr/lf are noise chars
26312         } else {
26313             this.text += c;
26314         }
26315     },
26316     
26317     parseEscapes: function (c)
26318     {
26319         if (c === '\\' || c === '{' || c === '}') {
26320             this.text += c;
26321             this.parserState = this.parseText;
26322         } else {
26323             this.parserState = this.parseControlSymbol;
26324             this.parseControlSymbol(c);
26325         }
26326     },
26327     parseControlSymbol: function(c)
26328     {
26329         if (c === '~') {
26330             this.text += '\u00a0'; // nbsp
26331             this.parserState = this.parseText
26332         } else if (c === '-') {
26333              this.text += '\u00ad'; // soft hyphen
26334         } else if (c === '_') {
26335             this.text += '\u2011'; // non-breaking hyphen
26336         } else if (c === '*') {
26337             this.emitIgnorable();
26338             this.parserState = this.parseText;
26339         } else if (c === "'") {
26340             this.parserState = this.parseHexChar;
26341         } else if (c === '|') { // formula cacter
26342             this.emitFormula();
26343             this.parserState = this.parseText;
26344         } else if (c === ':') { // subentry in an index entry
26345             this.emitIndexSubEntry();
26346             this.parserState = this.parseText;
26347         } else if (c === '\x0a') {
26348             this.emitEndParagraph();
26349             this.parserState = this.parseText;
26350         } else if (c === '\x0d') {
26351             this.emitEndParagraph();
26352             this.parserState = this.parseText;
26353         } else {
26354             this.parserState = this.parseControlWord;
26355             this.parseControlWord(c);
26356         }
26357     },
26358     parseHexChar: function (c)
26359     {
26360         if (/^[A-Fa-f0-9]$/.test(c)) {
26361             this.hexChar += c;
26362             if (this.hexChar.length >= 2) {
26363               this.emitHexChar();
26364               this.parserState = this.parseText;
26365             }
26366             return;
26367         }
26368         this.emitError("Invalid character \"" + c + "\" in hex literal.");
26369         this.parserState = this.parseText;
26370         
26371     },
26372     parseControlWord : function(c)
26373     {
26374         if (c === ' ') {
26375             this.emitControlWord();
26376             this.parserState = this.parseText;
26377         } else if (/^[-\d]$/.test(c)) {
26378             this.parserState = this.parseControlWordParam;
26379             this.controlWordParam += c;
26380         } else if (/^[A-Za-z]$/.test(c)) {
26381           this.controlWord += c;
26382         } else {
26383           this.emitControlWord();
26384           this.parserState = this.parseText;
26385           this.parseText(c);
26386         }
26387     },
26388     parseControlWordParam : function (c) {
26389         if (/^\d$/.test(c)) {
26390           this.controlWordParam += c;
26391         } else if (c === ' ') {
26392           this.emitControlWord();
26393           this.parserState = this.parseText;
26394         } else {
26395           this.emitControlWord();
26396           this.parserState = this.parseText;
26397           this.parseText(c);
26398         }
26399     },
26400     
26401     
26402     
26403     
26404     emitText : function () {
26405         if (this.text === '') {
26406             return;
26407         }
26408         this.push({
26409             type: 'text',
26410             value: this.text,
26411             pos: this.cpos,
26412             row: this.row,
26413             col: this.col
26414         });
26415         this.text = ''
26416     },
26417     emitControlWord : function ()
26418     {
26419         this.emitText();
26420         if (this.controlWord === '') {
26421             // do we want to track this - it seems just to cause problems.
26422             //this.emitError('empty control word');
26423         } else {
26424             this.push({
26425                   type: 'controlword',
26426                   value: this.controlWord,
26427                   param: this.controlWordParam !== '' && Number(this.controlWordParam),
26428                   pos: this.cpos,
26429                   row: this.row,
26430                   col: this.col
26431             });
26432         }
26433         this.controlWord = '';
26434         this.controlWordParam = '';
26435     },
26436     emitStartGroup : function ()
26437     {
26438         this.emitText();
26439         this.push({
26440             type: 'groupstart',
26441             pos: this.cpos,
26442             row: this.row,
26443             col: this.col
26444         });
26445     },
26446     emitEndGroup : function ()
26447     {
26448         this.emitText();
26449         this.push({
26450             type: 'groupend',
26451             pos: this.cpos,
26452             row: this.row,
26453             col: this.col
26454         });
26455     },
26456     emitIgnorable : function ()
26457     {
26458         this.emitText();
26459         this.push({
26460             type: 'ignorable',
26461             pos: this.cpos,
26462             row: this.row,
26463             col: this.col
26464         });
26465     },
26466     emitHexChar : function ()
26467     {
26468         this.emitText();
26469         this.push({
26470             type: 'hexchar',
26471             value: this.hexChar,
26472             pos: this.cpos,
26473             row: this.row,
26474             col: this.col
26475         });
26476         this.hexChar = ''
26477     },
26478     emitError : function (message)
26479     {
26480       this.emitText();
26481       this.push({
26482             type: 'error',
26483             value: message,
26484             row: this.row,
26485             col: this.col,
26486             char: this.cpos //,
26487             //stack: new Error().stack
26488         });
26489     },
26490     emitEndParagraph : function () {
26491         this.emitText();
26492         this.push({
26493             type: 'endparagraph',
26494             pos: this.cpos,
26495             row: this.row,
26496             col: this.col
26497         });
26498     }
26499      
26500 } ; 
26501 /**
26502  * @class Roo.htmleditor.Filter
26503  * Base Class for filtering htmleditor stuff. - do not use this directly - extend it.
26504  * @cfg {DomElement} node The node to iterate and filter
26505  * @cfg {boolean|String|Array} tag Tags to replace 
26506  * @constructor
26507  * Create a new Filter.
26508  * @param {Object} config Configuration options
26509  */
26510
26511
26512
26513 Roo.htmleditor.Filter = function(cfg) {
26514     Roo.apply(this.cfg);
26515     // this does not actually call walk as it's really just a abstract class
26516 }
26517
26518
26519 Roo.htmleditor.Filter.prototype = {
26520     
26521     node: false,
26522     
26523     tag: false,
26524
26525     // overrride to do replace comments.
26526     replaceComment : false,
26527     
26528     // overrride to do replace or do stuff with tags..
26529     replaceTag : false,
26530     
26531     walk : function(dom)
26532     {
26533         Roo.each( Array.from(dom.childNodes), function( e ) {
26534             switch(true) {
26535                 
26536                 case e.nodeType == 8 &&  this.replaceComment  !== false: // comment
26537                     this.replaceComment(e);
26538                     return;
26539                 
26540                 case e.nodeType != 1: //not a node.
26541                     return;
26542                 
26543                 case this.tag === true: // everything
26544                 case e.tagName.indexOf(":") > -1 && typeof(this.tag) == 'object' && this.tag.indexOf(":") > -1:
26545                 case e.tagName.indexOf(":") > -1 && typeof(this.tag) == 'string' && this.tag == ":":
26546                 case typeof(this.tag) == 'object' && this.tag.indexOf(e.tagName) > -1: // array and it matches.
26547                 case typeof(this.tag) == 'string' && this.tag == e.tagName: // array and it matches.
26548                     if (this.replaceTag && false === this.replaceTag(e)) {
26549                         return;
26550                     }
26551                     if (e.hasChildNodes()) {
26552                         this.walk(e);
26553                     }
26554                     return;
26555                 
26556                 default:    // tags .. that do not match.
26557                     if (e.hasChildNodes()) {
26558                         this.walk(e);
26559                     }
26560             }
26561             
26562         }, this);
26563         
26564     },
26565     
26566     
26567     removeNodeKeepChildren : function( node)
26568     {
26569     
26570         ar = Array.from(node.childNodes);
26571         for (var i = 0; i < ar.length; i++) {
26572          
26573             node.removeChild(ar[i]);
26574             // what if we need to walk these???
26575             node.parentNode.insertBefore(ar[i], node);
26576            
26577         }
26578         node.parentNode.removeChild(node);
26579     }
26580 }; 
26581
26582 /**
26583  * @class Roo.htmleditor.FilterAttributes
26584  * clean attributes and  styles including http:// etc.. in attribute
26585  * @constructor
26586 * Run a new Attribute Filter
26587 * @param {Object} config Configuration options
26588  */
26589 Roo.htmleditor.FilterAttributes = function(cfg)
26590 {
26591     Roo.apply(this, cfg);
26592     this.attrib_black = this.attrib_black || [];
26593     this.attrib_white = this.attrib_white || [];
26594
26595     this.attrib_clean = this.attrib_clean || [];
26596     this.style_white = this.style_white || [];
26597     this.style_black = this.style_black || [];
26598     this.walk(cfg.node);
26599 }
26600
26601 Roo.extend(Roo.htmleditor.FilterAttributes, Roo.htmleditor.Filter,
26602 {
26603     tag: true, // all tags
26604     
26605     attrib_black : false, // array
26606     attrib_clean : false,
26607     attrib_white : false,
26608
26609     style_white : false,
26610     style_black : false,
26611      
26612      
26613     replaceTag : function(node)
26614     {
26615         if (!node.attributes || !node.attributes.length) {
26616             return true;
26617         }
26618         
26619         for (var i = node.attributes.length-1; i > -1 ; i--) {
26620             var a = node.attributes[i];
26621             //console.log(a);
26622             if (this.attrib_white.length && this.attrib_white.indexOf(a.name.toLowerCase()) < 0) {
26623                 node.removeAttribute(a.name);
26624                 continue;
26625             }
26626             
26627             
26628             
26629             if (a.name.toLowerCase().substr(0,2)=='on')  {
26630                 node.removeAttribute(a.name);
26631                 continue;
26632             }
26633             
26634             
26635             if (this.attrib_black.indexOf(a.name.toLowerCase()) > -1) {
26636                 node.removeAttribute(a.name);
26637                 continue;
26638             }
26639             if (this.attrib_clean.indexOf(a.name.toLowerCase()) > -1) {
26640                 this.cleanAttr(node,a.name,a.value); // fixme..
26641                 continue;
26642             }
26643             if (a.name == 'style') {
26644                 this.cleanStyle(node,a.name,a.value);
26645                 continue;
26646             }
26647             /// clean up MS crap..
26648             // tecnically this should be a list of valid class'es..
26649             
26650             
26651             if (a.name == 'class') {
26652                 if (a.value.match(/^Mso/)) {
26653                     node.removeAttribute('class');
26654                 }
26655                 
26656                 if (a.value.match(/^body$/)) {
26657                     node.removeAttribute('class');
26658                 }
26659                 continue;
26660             }
26661             
26662             
26663             // style cleanup!?
26664             // class cleanup?
26665             
26666         }
26667         return true; // clean children
26668     },
26669         
26670     cleanAttr: function(node, n,v)
26671     {
26672         
26673         if (v.match(/^\./) || v.match(/^\//)) {
26674             return;
26675         }
26676         if (v.match(/^(http|https):\/\//)
26677             || v.match(/^mailto:/) 
26678             || v.match(/^ftp:/)
26679             || v.match(/^data:/)
26680             ) {
26681             return;
26682         }
26683         if (v.match(/^#/)) {
26684             return;
26685         }
26686         if (v.match(/^\{/)) { // allow template editing.
26687             return;
26688         }
26689 //            Roo.log("(REMOVE TAG)"+ node.tagName +'.' + n + '=' + v);
26690         node.removeAttribute(n);
26691         
26692     },
26693     cleanStyle : function(node,  n,v)
26694     {
26695         if (v.match(/expression/)) { //XSS?? should we even bother..
26696             node.removeAttribute(n);
26697             return;
26698         }
26699         
26700         var parts = v.split(/;/);
26701         var clean = [];
26702         
26703         Roo.each(parts, function(p) {
26704             p = p.replace(/^\s+/g,'').replace(/\s+$/g,'');
26705             if (!p.length) {
26706                 return true;
26707             }
26708             var l = p.split(':').shift().replace(/\s+/g,'');
26709             l = l.replace(/^\s+/g,'').replace(/\s+$/g,'');
26710             
26711             if ( this.style_black.length && (this.style_black.indexOf(l) > -1 || this.style_black.indexOf(l.toLowerCase()) > -1)) {
26712                 return true;
26713             }
26714             //Roo.log()
26715             // only allow 'c whitelisted system attributes'
26716             if ( this.style_white.length &&  style_white.indexOf(l) < 0 && style_white.indexOf(l.toLowerCase()) < 0 ) {
26717                 return true;
26718             }
26719             
26720             
26721             clean.push(p);
26722             return true;
26723         },this);
26724         if (clean.length) { 
26725             node.setAttribute(n, clean.join(';'));
26726         } else {
26727             node.removeAttribute(n);
26728         }
26729         
26730     }
26731         
26732         
26733         
26734     
26735 });/**
26736  * @class Roo.htmleditor.FilterBlack
26737  * remove blacklisted elements.
26738  * @constructor
26739  * Run a new Blacklisted Filter
26740  * @param {Object} config Configuration options
26741  */
26742
26743 Roo.htmleditor.FilterBlack = function(cfg)
26744 {
26745     Roo.apply(this, cfg);
26746     this.walk(cfg.node);
26747 }
26748
26749 Roo.extend(Roo.htmleditor.FilterBlack, Roo.htmleditor.Filter,
26750 {
26751     tag : true, // all elements.
26752    
26753     replaceTag : function(n)
26754     {
26755         n.parentNode.removeChild(n);
26756     }
26757 });
26758 /**
26759  * @class Roo.htmleditor.FilterComment
26760  * remove comments.
26761  * @constructor
26762 * Run a new Comments Filter
26763 * @param {Object} config Configuration options
26764  */
26765 Roo.htmleditor.FilterComment = function(cfg)
26766 {
26767     this.walk(cfg.node);
26768 }
26769
26770 Roo.extend(Roo.htmleditor.FilterComment, Roo.htmleditor.Filter,
26771 {
26772   
26773     replaceComment : function(n)
26774     {
26775         n.parentNode.removeChild(n);
26776     }
26777 });/**
26778  * @class Roo.htmleditor.FilterKeepChildren
26779  * remove tags but keep children
26780  * @constructor
26781  * Run a new Keep Children Filter
26782  * @param {Object} config Configuration options
26783  */
26784
26785 Roo.htmleditor.FilterKeepChildren = function(cfg)
26786 {
26787     Roo.apply(this, cfg);
26788     if (this.tag === false) {
26789         return; // dont walk.. (you can use this to use this just to do a child removal on a single tag )
26790     }
26791     // hacky?
26792     if ((typeof(this.tag) == 'object' && this.tag.indexOf(":") > -1)) {
26793         this.cleanNamespace = true;
26794     }
26795         
26796     this.walk(cfg.node);
26797 }
26798
26799 Roo.extend(Roo.htmleditor.FilterKeepChildren, Roo.htmleditor.FilterBlack,
26800 {
26801     cleanNamespace : false, // should really be an option, rather than using ':' inside of this tag.
26802   
26803     replaceTag : function(node)
26804     {
26805         // walk children...
26806         //Roo.log(node.tagName);
26807         var ar = Array.from(node.childNodes);
26808         //remove first..
26809         
26810         for (var i = 0; i < ar.length; i++) {
26811             var e = ar[i];
26812             if (e.nodeType == 1) {
26813                 if (
26814                     (typeof(this.tag) == 'object' && this.tag.indexOf(e.tagName) > -1)
26815                     || // array and it matches
26816                     (typeof(this.tag) == 'string' && this.tag == e.tagName)
26817                     ||
26818                     (e.tagName.indexOf(":") > -1 && typeof(this.tag) == 'object' && this.tag.indexOf(":") > -1)
26819                     ||
26820                     (e.tagName.indexOf(":") > -1 && typeof(this.tag) == 'string' && this.tag == ":")
26821                 ) {
26822                     this.replaceTag(ar[i]); // child is blacklisted as well...
26823                     continue;
26824                 }
26825             }
26826         }  
26827         ar = Array.from(node.childNodes);
26828         for (var i = 0; i < ar.length; i++) {
26829          
26830             node.removeChild(ar[i]);
26831             // what if we need to walk these???
26832             node.parentNode.insertBefore(ar[i], node);
26833             if (this.tag !== false) {
26834                 this.walk(ar[i]);
26835                 
26836             }
26837         }
26838         //Roo.log("REMOVE:" + node.tagName);
26839         node.parentNode.removeChild(node);
26840         return false; // don't walk children
26841         
26842         
26843     }
26844 });/**
26845  * @class Roo.htmleditor.FilterParagraph
26846  * paragraphs cause a nightmare for shared content - this filter is designed to be called ? at various points when editing
26847  * like on 'push' to remove the <p> tags and replace them with line breaks.
26848  * @constructor
26849  * Run a new Paragraph Filter
26850  * @param {Object} config Configuration options
26851  */
26852
26853 Roo.htmleditor.FilterParagraph = function(cfg)
26854 {
26855     // no need to apply config.
26856     this.walk(cfg.node);
26857 }
26858
26859 Roo.extend(Roo.htmleditor.FilterParagraph, Roo.htmleditor.Filter,
26860 {
26861     
26862      
26863     tag : 'P',
26864     
26865      
26866     replaceTag : function(node)
26867     {
26868         
26869         if (node.childNodes.length == 1 &&
26870             node.childNodes[0].nodeType == 3 &&
26871             node.childNodes[0].textContent.trim().length < 1
26872             ) {
26873             // remove and replace with '<BR>';
26874             node.parentNode.replaceChild(node.ownerDocument.createElement('BR'),node);
26875             return false; // no need to walk..
26876         }
26877         var ar = Array.from(node.childNodes);
26878         for (var i = 0; i < ar.length; i++) {
26879             node.removeChild(ar[i]);
26880             // what if we need to walk these???
26881             node.parentNode.insertBefore(ar[i], node);
26882         }
26883         // now what about this?
26884         // <p> &nbsp; </p>
26885         
26886         // double BR.
26887         node.parentNode.insertBefore(node.ownerDocument.createElement('BR'), node);
26888         node.parentNode.insertBefore(node.ownerDocument.createElement('BR'), node);
26889         node.parentNode.removeChild(node);
26890         
26891         return false;
26892
26893     }
26894     
26895 });/**
26896  * @class Roo.htmleditor.FilterSpan
26897  * filter span's with no attributes out..
26898  * @constructor
26899  * Run a new Span Filter
26900  * @param {Object} config Configuration options
26901  */
26902
26903 Roo.htmleditor.FilterSpan = function(cfg)
26904 {
26905     // no need to apply config.
26906     this.walk(cfg.node);
26907 }
26908
26909 Roo.extend(Roo.htmleditor.FilterSpan, Roo.htmleditor.FilterKeepChildren,
26910 {
26911      
26912     tag : 'SPAN',
26913      
26914  
26915     replaceTag : function(node)
26916     {
26917         if (node.attributes && node.attributes.length > 0) {
26918             return true; // walk if there are any.
26919         }
26920         Roo.htmleditor.FilterKeepChildren.prototype.replaceTag.call(this, node);
26921         return false;
26922      
26923     }
26924     
26925 });/**
26926  * @class Roo.htmleditor.FilterTableWidth
26927   try and remove table width data - as that frequently messes up other stuff.
26928  * 
26929  *      was cleanTableWidths.
26930  *
26931  * Quite often pasting from word etc.. results in tables with column and widths.
26932  * This does not work well on fluid HTML layouts - like emails. - so this code should hunt an destroy them..
26933  *
26934  * @constructor
26935  * Run a new Table Filter
26936  * @param {Object} config Configuration options
26937  */
26938
26939 Roo.htmleditor.FilterTableWidth = function(cfg)
26940 {
26941     // no need to apply config.
26942     this.tag = ['TABLE', 'TD', 'TR', 'TH', 'THEAD', 'TBODY' ];
26943     this.walk(cfg.node);
26944 }
26945
26946 Roo.extend(Roo.htmleditor.FilterTableWidth, Roo.htmleditor.Filter,
26947 {
26948      
26949      
26950     
26951     replaceTag: function(node) {
26952         
26953         
26954       
26955         if (node.hasAttribute('width')) {
26956             node.removeAttribute('width');
26957         }
26958         
26959          
26960         if (node.hasAttribute("style")) {
26961             // pretty basic...
26962             
26963             var styles = node.getAttribute("style").split(";");
26964             var nstyle = [];
26965             Roo.each(styles, function(s) {
26966                 if (!s.match(/:/)) {
26967                     return;
26968                 }
26969                 var kv = s.split(":");
26970                 if (kv[0].match(/^\s*(width|min-width)\s*$/)) {
26971                     return;
26972                 }
26973                 // what ever is left... we allow.
26974                 nstyle.push(s);
26975             });
26976             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
26977             if (!nstyle.length) {
26978                 node.removeAttribute('style');
26979             }
26980         }
26981         
26982         return true; // continue doing children..
26983     }
26984 });/**
26985  * @class Roo.htmleditor.FilterWord
26986  * try and clean up all the mess that Word generates.
26987  * 
26988  * This is the 'nice version' - see 'Heavy' that white lists a very short list of elements, and multi-filters 
26989  
26990  * @constructor
26991  * Run a new Span Filter
26992  * @param {Object} config Configuration options
26993  */
26994
26995 Roo.htmleditor.FilterWord = function(cfg)
26996 {
26997     // no need to apply config.
26998     this.replaceDocBullets(cfg.node);
26999     
27000     this.replaceAname(cfg.node);
27001     // this is disabled as the removal is done by other filters;
27002    // this.walk(cfg.node);
27003     this.replaceImageTable(cfg.node);
27004     
27005 }
27006
27007 Roo.extend(Roo.htmleditor.FilterWord, Roo.htmleditor.Filter,
27008 {
27009     tag: true,
27010      
27011     
27012     /**
27013      * Clean up MS wordisms...
27014      */
27015     replaceTag : function(node)
27016     {
27017          
27018         // no idea what this does - span with text, replaceds with just text.
27019         if(
27020                 node.nodeName == 'SPAN' &&
27021                 !node.hasAttributes() &&
27022                 node.childNodes.length == 1 &&
27023                 node.firstChild.nodeName == "#text"  
27024         ) {
27025             var textNode = node.firstChild;
27026             node.removeChild(textNode);
27027             if (node.getAttribute('lang') != 'zh-CN') {   // do not space pad on chinese characters..
27028                 node.parentNode.insertBefore(node.ownerDocument.createTextNode(" "), node);
27029             }
27030             node.parentNode.insertBefore(textNode, node);
27031             if (node.getAttribute('lang') != 'zh-CN') {   // do not space pad on chinese characters..
27032                 node.parentNode.insertBefore(node.ownerDocument.createTextNode(" ") , node);
27033             }
27034             
27035             node.parentNode.removeChild(node);
27036             return false; // dont do chidren - we have remove our node - so no need to do chdhilren?
27037         }
27038         
27039    
27040         
27041         if (node.tagName.toLowerCase().match(/^(style|script|applet|embed|noframes|noscript)$/)) {
27042             node.parentNode.removeChild(node);
27043             return false; // dont do chidlren
27044         }
27045         //Roo.log(node.tagName);
27046         // remove - but keep children..
27047         if (node.tagName.toLowerCase().match(/^(meta|link|\\?xml:|st1:|o:|v:|font)/)) {
27048             //Roo.log('-- removed');
27049             while (node.childNodes.length) {
27050                 var cn = node.childNodes[0];
27051                 node.removeChild(cn);
27052                 node.parentNode.insertBefore(cn, node);
27053                 // move node to parent - and clean it..
27054                 if (cn.nodeType == 1) {
27055                     this.replaceTag(cn);
27056                 }
27057                 
27058             }
27059             node.parentNode.removeChild(node);
27060             /// no need to iterate chidlren = it's got none..
27061             //this.iterateChildren(node, this.cleanWord);
27062             return false; // no need to iterate children.
27063         }
27064         // clean styles
27065         if (node.className.length) {
27066             
27067             var cn = node.className.split(/\W+/);
27068             var cna = [];
27069             Roo.each(cn, function(cls) {
27070                 if (cls.match(/Mso[a-zA-Z]+/)) {
27071                     return;
27072                 }
27073                 cna.push(cls);
27074             });
27075             node.className = cna.length ? cna.join(' ') : '';
27076             if (!cna.length) {
27077                 node.removeAttribute("class");
27078             }
27079         }
27080         
27081         if (node.hasAttribute("lang")) {
27082             node.removeAttribute("lang");
27083         }
27084         
27085         if (node.hasAttribute("style")) {
27086             
27087             var styles = node.getAttribute("style").split(";");
27088             var nstyle = [];
27089             Roo.each(styles, function(s) {
27090                 if (!s.match(/:/)) {
27091                     return;
27092                 }
27093                 var kv = s.split(":");
27094                 if (kv[0].match(/^(mso-|line|font|background|margin|padding|color)/)) {
27095                     return;
27096                 }
27097                 // what ever is left... we allow.
27098                 nstyle.push(s);
27099             });
27100             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
27101             if (!nstyle.length) {
27102                 node.removeAttribute('style');
27103             }
27104         }
27105         return true; // do children
27106         
27107         
27108         
27109     },
27110     
27111     styleToObject: function(node)
27112     {
27113         var styles = (node.getAttribute("style") || '').split(";");
27114         var ret = {};
27115         Roo.each(styles, function(s) {
27116             if (!s.match(/:/)) {
27117                 return;
27118             }
27119             var kv = s.split(":");
27120              
27121             // what ever is left... we allow.
27122             ret[kv[0].trim()] = kv[1];
27123         });
27124         return ret;
27125     },
27126     
27127     
27128     replaceAname : function (doc)
27129     {
27130         // replace all the a/name without..
27131         var aa = Array.from(doc.getElementsByTagName('a'));
27132         for (var i = 0; i  < aa.length; i++) {
27133             var a = aa[i];
27134             if (a.hasAttribute("name")) {
27135                 a.removeAttribute("name");
27136             }
27137             if (a.hasAttribute("href")) {
27138                 continue;
27139             }
27140             // reparent children.
27141             this.removeNodeKeepChildren(a);
27142             
27143         }
27144         
27145         
27146         
27147     },
27148
27149     
27150     
27151     replaceDocBullets : function(doc)
27152     {
27153         // this is a bit odd - but it appears some indents use ql-indent-1
27154          //Roo.log(doc.innerHTML);
27155         
27156         var listpara = Array.from(doc.getElementsByClassName('MsoListParagraphCxSpFirst'));
27157         for( var i = 0; i < listpara.length; i ++) {
27158             listpara[i].className = "MsoListParagraph";
27159         }
27160         
27161         listpara =  Array.from(doc.getElementsByClassName('MsoListParagraphCxSpMiddle'));
27162         for( var i = 0; i < listpara.length; i ++) {
27163             listpara[i].className = "MsoListParagraph";
27164         }
27165         listpara =  Array.from(doc.getElementsByClassName('MsoListParagraphCxSpLast'));
27166         for( var i = 0; i < listpara.length; i ++) {
27167             listpara[i].className = "MsoListParagraph";
27168         }
27169         listpara =  Array.from(doc.getElementsByClassName('ql-indent-1'));
27170         for( var i = 0; i < listpara.length; i ++) {
27171             listpara[i].className = "MsoListParagraph";
27172         }
27173         
27174         // this is a bit hacky - we had one word document where h2 had a miso-list attribute.
27175         var htwo =  Array.from(doc.getElementsByTagName('h2'));
27176         for( var i = 0; i < htwo.length; i ++) {
27177             if (htwo[i].hasAttribute('style') && htwo[i].getAttribute('style').match(/mso-list:/)) {
27178                 htwo[i].className = "MsoListParagraph";
27179             }
27180         }
27181         listpara =  Array.from(doc.getElementsByClassName('MsoNormal'));
27182         for( var i = 0; i < listpara.length; i ++) {
27183             if (listpara[i].hasAttribute('style') && listpara[i].getAttribute('style').match(/mso-list:/)) {
27184                 listpara[i].className = "MsoListParagraph";
27185             } else {
27186                 listpara[i].className = "MsoNormalx";
27187             }
27188         }
27189        
27190         listpara = doc.getElementsByClassName('MsoListParagraph');
27191         // Roo.log(doc.innerHTML);
27192         
27193         
27194         
27195         while(listpara.length) {
27196             
27197             this.replaceDocBullet(listpara.item(0));
27198         }
27199       
27200     },
27201     
27202      
27203     
27204     replaceDocBullet : function(p)
27205     {
27206         // gather all the siblings.
27207         var ns = p,
27208             parent = p.parentNode,
27209             doc = parent.ownerDocument,
27210             items = [];
27211          
27212         //Roo.log("Parsing: " + p.innerText)    ;
27213         var listtype = 'ul';   
27214         while (ns) {
27215             if (ns.nodeType != 1) {
27216                 ns = ns.nextSibling;
27217                 continue;
27218             }
27219             if (!ns.className.match(/(MsoListParagraph|ql-indent-1)/i)) {
27220                 //Roo.log("Missing para r q1indent - got:" + ns.className);
27221                 break;
27222             }
27223             var spans = ns.getElementsByTagName('span');
27224             
27225             if (ns.hasAttribute('style') && ns.getAttribute('style').match(/mso-list/)) {
27226                 items.push(ns);
27227                 ns = ns.nextSibling;
27228                 has_list = true;
27229                 if (!spans.length) {
27230                     continue;
27231                 }
27232                 var ff = '';
27233                 var se = spans[0];
27234                 for (var i = 0; i < spans.length;i++) {
27235                     se = spans[i];
27236                     if (se.hasAttribute('style')  && se.hasAttribute('style') && se.style.fontFamily != '') {
27237                         ff = se.style.fontFamily;
27238                         break;
27239                     }
27240                 }
27241                  
27242                     
27243                 //Roo.log("got font family: " + ff);
27244                 if (typeof(ff) != 'undefined' && !ff.match(/(Symbol|Wingdings)/) && "·o".indexOf(se.innerText.trim()) < 0) {
27245                     listtype = 'ol';
27246                 }
27247                 
27248                 continue;
27249             }
27250             //Roo.log("no mso-list?");
27251             
27252             var spans = ns.getElementsByTagName('span');
27253             if (!spans.length) {
27254                 break;
27255             }
27256             var has_list  = false;
27257             for(var i = 0; i < spans.length; i++) {
27258                 if (spans[i].hasAttribute('style') && spans[i].getAttribute('style').match(/mso-list/)) {
27259                     has_list = true;
27260                     break;
27261                 }
27262             }
27263             if (!has_list) {
27264                 break;
27265             }
27266             items.push(ns);
27267             ns = ns.nextSibling;
27268             
27269             
27270         }
27271         if (!items.length) {
27272             ns.className = "";
27273             return;
27274         }
27275         
27276         var ul = parent.ownerDocument.createElement(listtype); // what about number lists...
27277         parent.insertBefore(ul, p);
27278         var lvl = 0;
27279         var stack = [ ul ];
27280         var last_li = false;
27281         
27282         var margin_to_depth = {};
27283         max_margins = -1;
27284         
27285         items.forEach(function(n, ipos) {
27286             //Roo.log("got innertHMLT=" + n.innerHTML);
27287             
27288             var spans = n.getElementsByTagName('span');
27289             if (!spans.length) {
27290                 //Roo.log("No spans found");
27291                  
27292                 parent.removeChild(n);
27293                 
27294                 
27295                 return; // skip it...
27296             }
27297            
27298                 
27299             var num = 1;
27300             var style = {};
27301             for(var i = 0; i < spans.length; i++) {
27302             
27303                 style = this.styleToObject(spans[i]);
27304                 if (typeof(style['mso-list']) == 'undefined') {
27305                     continue;
27306                 }
27307                 if (listtype == 'ol') {
27308                    num = spans[i].innerText.replace(/[^0-9]+]/g,'')  * 1;
27309                 }
27310                 spans[i].parentNode.removeChild(spans[i]); // remove the fake bullet.
27311                 break;
27312             }
27313             //Roo.log("NOW GOT innertHMLT=" + n.innerHTML);
27314             style = this.styleToObject(n); // mo-list is from the parent node.
27315             if (typeof(style['mso-list']) == 'undefined') {
27316                 //Roo.log("parent is missing level");
27317                   
27318                 parent.removeChild(n);
27319                  
27320                 return;
27321             }
27322             
27323             var margin = style['margin-left'];
27324             if (typeof(margin_to_depth[margin]) == 'undefined') {
27325                 max_margins++;
27326                 margin_to_depth[margin] = max_margins;
27327             }
27328             nlvl = margin_to_depth[margin] ;
27329              
27330             if (nlvl > lvl) {
27331                 //new indent
27332                 var nul = doc.createElement(listtype); // what about number lists...
27333                 if (!last_li) {
27334                     last_li = doc.createElement('li');
27335                     stack[lvl].appendChild(last_li);
27336                 }
27337                 last_li.appendChild(nul);
27338                 stack[nlvl] = nul;
27339                 
27340             }
27341             lvl = nlvl;
27342             
27343             // not starting at 1..
27344             if (!stack[nlvl].hasAttribute("start") && listtype == "ol") {
27345                 stack[nlvl].setAttribute("start", num);
27346             }
27347             
27348             var nli = stack[nlvl].appendChild(doc.createElement('li'));
27349             last_li = nli;
27350             nli.innerHTML = n.innerHTML;
27351             //Roo.log("innerHTML = " + n.innerHTML);
27352             parent.removeChild(n);
27353             
27354              
27355              
27356             
27357         },this);
27358         
27359         
27360         
27361         
27362     },
27363     
27364     replaceImageTable : function(doc)
27365     {
27366          /*
27367           <table cellpadding=0 cellspacing=0 align=left>
27368   <tr>
27369    <td width=423 height=0></td>
27370   </tr>
27371   <tr>
27372    <td></td>
27373    <td><img width=601 height=401
27374    src="file:///C:/Users/Alan/AppData/Local/Temp/msohtmlclip1/01/clip_image002.jpg"
27375    v:shapes="Picture_x0020_2"></td>
27376   </tr>
27377  </table>
27378  */
27379         var imgs = Array.from(doc.getElementsByTagName('img'));
27380         Roo.each(imgs, function(img) {
27381             var td = img.parentNode;
27382             if (td.nodeName !=  'TD') {
27383                 return;
27384             }
27385             var tr = td.parentNode;
27386             if (tr.nodeName !=  'TR') {
27387                 return;
27388             }
27389             var tbody = tr.parentNode;
27390             if (tbody.nodeName !=  'TBODY') {
27391                 return;
27392             }
27393             var table = tbody.parentNode;
27394             if (table.nodeName !=  'TABLE') {
27395                 return;
27396             }
27397             // first row..
27398             
27399             if (table.getElementsByTagName('tr').length != 2) {
27400                 return;
27401             }
27402             if (table.getElementsByTagName('td').length != 3) {
27403                 return;
27404             }
27405             if (table.innerText.trim() != '') {
27406                 return;
27407             }
27408             var p = table.parentNode;
27409             img.parentNode.removeChild(img);
27410             p.insertBefore(img, table);
27411             p.removeChild(table);
27412             
27413             
27414             
27415         });
27416         
27417       
27418     }
27419     
27420 });
27421 /**
27422  * @class Roo.htmleditor.FilterStyleToTag
27423  * part of the word stuff... - certain 'styles' should be converted to tags.
27424  * eg.
27425  *   font-weight: bold -> bold
27426  *   ?? super / subscrit etc..
27427  * 
27428  * @constructor
27429 * Run a new style to tag filter.
27430 * @param {Object} config Configuration options
27431  */
27432 Roo.htmleditor.FilterStyleToTag = function(cfg)
27433 {
27434     
27435     this.tags = {
27436         B  : [ 'fontWeight' , 'bold'],
27437         I :  [ 'fontStyle' , 'italic'],
27438         //pre :  [ 'font-style' , 'italic'],
27439         // h1.. h6 ?? font-size?
27440         SUP : [ 'verticalAlign' , 'super' ],
27441         SUB : [ 'verticalAlign' , 'sub' ]
27442         
27443         
27444     };
27445     
27446     Roo.apply(this, cfg);
27447      
27448     
27449     this.walk(cfg.node);
27450     
27451     
27452     
27453 }
27454
27455
27456 Roo.extend(Roo.htmleditor.FilterStyleToTag, Roo.htmleditor.Filter,
27457 {
27458     tag: true, // all tags
27459     
27460     tags : false,
27461     
27462     
27463     replaceTag : function(node)
27464     {
27465         
27466         
27467         if (node.getAttribute("style") === null) {
27468             return true;
27469         }
27470         var inject = [];
27471         for (var k in this.tags) {
27472             if (node.style[this.tags[k][0]] == this.tags[k][1]) {
27473                 inject.push(k);
27474                 node.style.removeProperty(this.tags[k][0]);
27475             }
27476         }
27477         if (!inject.length) {
27478             return true; 
27479         }
27480         var cn = Array.from(node.childNodes);
27481         var nn = node;
27482         Roo.each(inject, function(t) {
27483             var nc = node.ownerDocument.createElement(t);
27484             nn.appendChild(nc);
27485             nn = nc;
27486         });
27487         for(var i = 0;i < cn.length;cn++) {
27488             node.removeChild(cn[i]);
27489             nn.appendChild(cn[i]);
27490         }
27491         return true /// iterate thru
27492     }
27493     
27494 })/**
27495  * @class Roo.htmleditor.FilterLongBr
27496  * BR/BR/BR - keep a maximum of 2...
27497  * @constructor
27498  * Run a new Long BR Filter
27499  * @param {Object} config Configuration options
27500  */
27501
27502 Roo.htmleditor.FilterLongBr = function(cfg)
27503 {
27504     // no need to apply config.
27505     this.walk(cfg.node);
27506 }
27507
27508 Roo.extend(Roo.htmleditor.FilterLongBr, Roo.htmleditor.Filter,
27509 {
27510     
27511      
27512     tag : 'BR',
27513     
27514      
27515     replaceTag : function(node)
27516     {
27517         
27518         var ps = node.nextSibling;
27519         while (ps && ps.nodeType == 3 && ps.nodeValue.trim().length < 1) {
27520             ps = ps.nextSibling;
27521         }
27522         
27523         if (!ps &&  [ 'TD', 'TH', 'LI', 'H1', 'H2', 'H3', 'H4', 'H5', 'H6' ].indexOf(node.parentNode.tagName) > -1) { 
27524             node.parentNode.removeChild(node); // remove last BR inside one fo these tags
27525             return false;
27526         }
27527         
27528         if (!ps || ps.nodeType != 1) {
27529             return false;
27530         }
27531         
27532         if (!ps || ps.tagName != 'BR') {
27533            
27534             return false;
27535         }
27536         
27537         
27538         
27539         
27540         
27541         if (!node.previousSibling) {
27542             return false;
27543         }
27544         var ps = node.previousSibling;
27545         
27546         while (ps && ps.nodeType == 3 && ps.nodeValue.trim().length < 1) {
27547             ps = ps.previousSibling;
27548         }
27549         if (!ps || ps.nodeType != 1) {
27550             return false;
27551         }
27552         // if header or BR before.. then it's a candidate for removal.. - as we only want '2' of these..
27553         if (!ps || [ 'BR', 'H1', 'H2', 'H3', 'H4', 'H5', 'H6' ].indexOf(ps.tagName) < 0) {
27554             return false;
27555         }
27556         
27557         node.parentNode.removeChild(node); // remove me...
27558         
27559         return false; // no need to do children
27560
27561     }
27562     
27563 }); 
27564
27565 /**
27566  * @class Roo.htmleditor.FilterBlock
27567  * removes id / data-block and contenteditable that are associated with blocks
27568  * usage should be done on a cloned copy of the dom
27569  * @constructor
27570 * Run a new Attribute Filter { node : xxxx }}
27571 * @param {Object} config Configuration options
27572  */
27573 Roo.htmleditor.FilterBlock = function(cfg)
27574 {
27575     Roo.apply(this, cfg);
27576     var qa = cfg.node.querySelectorAll;
27577     this.removeAttributes('data-block');
27578     this.removeAttributes('contenteditable');
27579     this.removeAttributes('id');
27580     
27581 }
27582
27583 Roo.apply(Roo.htmleditor.FilterBlock.prototype,
27584 {
27585     node: true, // all tags
27586      
27587      
27588     removeAttributes : function(attr)
27589     {
27590         var ar = this.node.querySelectorAll('*[' + attr + ']');
27591         for (var i =0;i<ar.length;i++) {
27592             ar[i].removeAttribute(attr);
27593         }
27594     }
27595         
27596         
27597         
27598     
27599 });
27600 /***
27601  * This is based loosely on tinymce 
27602  * @class Roo.htmleditor.TidySerializer
27603  * https://github.com/thorn0/tinymce.html/blob/master/tinymce.html.js
27604  * @constructor
27605  * @method Serializer
27606  * @param {Object} settings Name/value settings object.
27607  */
27608
27609
27610 Roo.htmleditor.TidySerializer = function(settings)
27611 {
27612     Roo.apply(this, settings);
27613     
27614     this.writer = new Roo.htmleditor.TidyWriter(settings);
27615     
27616     
27617
27618 };
27619 Roo.htmleditor.TidySerializer.prototype = {
27620     
27621     /**
27622      * @param {boolean} inner do the inner of the node.
27623      */
27624     inner : false,
27625     
27626     writer : false,
27627     
27628     /**
27629     * Serializes the specified node into a string.
27630     *
27631     * @example
27632     * new tinymce.html.Serializer().serialize(new tinymce.html.DomParser().parse('<p>text</p>'));
27633     * @method serialize
27634     * @param {DomElement} node Node instance to serialize.
27635     * @return {String} String with HTML based on DOM tree.
27636     */
27637     serialize : function(node) {
27638         
27639         // = settings.validate;
27640         var writer = this.writer;
27641         var self  = this;
27642         this.handlers = {
27643             // #text
27644             3: function(node) {
27645                 
27646                 writer.text(node.nodeValue, node);
27647             },
27648             // #comment
27649             8: function(node) {
27650                 writer.comment(node.nodeValue);
27651             },
27652             // Processing instruction
27653             7: function(node) {
27654                 writer.pi(node.name, node.nodeValue);
27655             },
27656             // Doctype
27657             10: function(node) {
27658                 writer.doctype(node.nodeValue);
27659             },
27660             // CDATA
27661             4: function(node) {
27662                 writer.cdata(node.nodeValue);
27663             },
27664             // Document fragment
27665             11: function(node) {
27666                 node = node.firstChild;
27667                 if (!node) {
27668                     return;
27669                 }
27670                 while(node) {
27671                     self.walk(node);
27672                     node = node.nextSibling
27673                 }
27674             }
27675         };
27676         writer.reset();
27677         1 != node.nodeType || this.inner ? this.handlers[11](node) : this.walk(node);
27678         return writer.getContent();
27679     },
27680
27681     walk: function(node)
27682     {
27683         var attrName, attrValue, sortedAttrs, i, l, elementRule,
27684             handler = this.handlers[node.nodeType];
27685             
27686         if (handler) {
27687             handler(node);
27688             return;
27689         }
27690     
27691         var name = node.nodeName;
27692         var isEmpty = node.childNodes.length < 1;
27693       
27694         var writer = this.writer;
27695         var attrs = node.attributes;
27696         // Sort attributes
27697         
27698         writer.start(node.nodeName, attrs, isEmpty, node);
27699         if (isEmpty) {
27700             return;
27701         }
27702         node = node.firstChild;
27703         if (!node) {
27704             writer.end(name);
27705             return;
27706         }
27707         while (node) {
27708             this.walk(node);
27709             node = node.nextSibling;
27710         }
27711         writer.end(name);
27712         
27713     
27714     }
27715     // Serialize element and treat all non elements as fragments
27716    
27717 }; 
27718
27719 /***
27720  * This is based loosely on tinymce 
27721  * @class Roo.htmleditor.TidyWriter
27722  * https://github.com/thorn0/tinymce.html/blob/master/tinymce.html.js
27723  *
27724  * Known issues?
27725  * - not tested much with 'PRE' formated elements.
27726  * 
27727  *
27728  *
27729  */
27730
27731 Roo.htmleditor.TidyWriter = function(settings)
27732 {
27733     
27734     // indent, indentBefore, indentAfter, encode, htmlOutput, html = [];
27735     Roo.apply(this, settings);
27736     this.html = [];
27737     this.state = [];
27738      
27739     this.encode = Roo.htmleditor.TidyEntities.getEncodeFunc(settings.entity_encoding || 'raw', settings.entities);
27740   
27741 }
27742 Roo.htmleditor.TidyWriter.prototype = {
27743
27744  
27745     state : false,
27746     
27747     indent :  '  ',
27748     
27749     // part of state...
27750     indentstr : '',
27751     in_pre: false,
27752     in_inline : false,
27753     last_inline : false,
27754     encode : false,
27755      
27756     
27757             /**
27758     * Writes the a start element such as <p id="a">.
27759     *
27760     * @method start
27761     * @param {String} name Name of the element.
27762     * @param {Array} attrs Optional attribute array or undefined if it hasn't any.
27763     * @param {Boolean} empty Optional empty state if the tag should end like <br />.
27764     */
27765     start: function(name, attrs, empty, node)
27766     {
27767         var i, l, attr, value;
27768         
27769         // there are some situations where adding line break && indentation will not work. will not work.
27770         // <span / b / i ... formating?
27771         
27772         var in_inline = this.in_inline || Roo.htmleditor.TidyWriter.inline_elements.indexOf(name) > -1;
27773         var in_pre    = this.in_pre    || Roo.htmleditor.TidyWriter.whitespace_elements.indexOf(name) > -1;
27774         
27775         var is_short   = empty ? Roo.htmleditor.TidyWriter.shortend_elements.indexOf(name) > -1 : false;
27776         
27777         var add_lb = name == 'BR' ? false : in_inline;
27778         
27779         if (!add_lb && !this.in_pre && this.lastElementEndsWS()) {
27780             i_inline = false;
27781         }
27782
27783         var indentstr =  this.indentstr;
27784         
27785         // e_inline = elements that can be inline, but still allow \n before and after?
27786         // only 'BR' ??? any others?
27787         
27788         // ADD LINE BEFORE tage
27789         if (!this.in_pre) {
27790             if (in_inline) {
27791                 //code
27792                 if (name == 'BR') {
27793                     this.addLine();
27794                 } else if (this.lastElementEndsWS()) {
27795                     this.addLine();
27796                 } else{
27797                     // otherwise - no new line. (and dont indent.)
27798                     indentstr = '';
27799                 }
27800                 
27801             } else {
27802                 this.addLine();
27803             }
27804         } else {
27805             indentstr = '';
27806         }
27807         
27808         this.html.push(indentstr + '<', name.toLowerCase());
27809         
27810         if (attrs) {
27811             for (i = 0, l = attrs.length; i < l; i++) {
27812                 attr = attrs[i];
27813                 this.html.push(' ', attr.name, '="', this.encode(attr.value, true), '"');
27814             }
27815         }
27816      
27817         if (empty) {
27818             if (is_short) {
27819                 this.html[this.html.length] = '/>';
27820             } else {
27821                 this.html[this.html.length] = '></' + name.toLowerCase() + '>';
27822             }
27823             var e_inline = name == 'BR' ? false : this.in_inline;
27824             
27825             if (!e_inline && !this.in_pre) {
27826                 this.addLine();
27827             }
27828             return;
27829         
27830         }
27831         // not empty..
27832         this.html[this.html.length] = '>';
27833         
27834         // there is a special situation, where we need to turn on in_inline - if any of the imediate chidlren are one of these.
27835         /*
27836         if (!in_inline && !in_pre) {
27837             var cn = node.firstChild;
27838             while(cn) {
27839                 if (Roo.htmleditor.TidyWriter.inline_elements.indexOf(cn.nodeName) > -1) {
27840                     in_inline = true
27841                     break;
27842                 }
27843                 cn = cn.nextSibling;
27844             }
27845              
27846         }
27847         */
27848         
27849         
27850         this.pushState({
27851             indentstr : in_pre   ? '' : (this.indentstr + this.indent),
27852             in_pre : in_pre,
27853             in_inline :  in_inline
27854         });
27855         // add a line after if we are not in a
27856         
27857         if (!in_inline && !in_pre) {
27858             this.addLine();
27859         }
27860         
27861             
27862          
27863         
27864     },
27865     
27866     lastElementEndsWS : function()
27867     {
27868         var value = this.html.length > 0 ? this.html[this.html.length-1] : false;
27869         if (value === false) {
27870             return true;
27871         }
27872         return value.match(/\s+$/);
27873         
27874     },
27875     
27876     /**
27877      * Writes the a end element such as </p>.
27878      *
27879      * @method end
27880      * @param {String} name Name of the element.
27881      */
27882     end: function(name) {
27883         var value;
27884         this.popState();
27885         var indentstr = '';
27886         var in_inline = this.in_inline || Roo.htmleditor.TidyWriter.inline_elements.indexOf(name) > -1;
27887         
27888         if (!this.in_pre && !in_inline) {
27889             this.addLine();
27890             indentstr  = this.indentstr;
27891         }
27892         this.html.push(indentstr + '</', name.toLowerCase(), '>');
27893         this.last_inline = in_inline;
27894         
27895         // pop the indent state..
27896     },
27897     /**
27898      * Writes a text node.
27899      *
27900      * In pre - we should not mess with the contents.
27901      * 
27902      *
27903      * @method text
27904      * @param {String} text String to write out.
27905      * @param {Boolean} raw Optional raw state if true the contents wont get encoded.
27906      */
27907     text: function(in_text, node)
27908     {
27909         // if not in whitespace critical
27910         if (in_text.length < 1) {
27911             return;
27912         }
27913         var text = new XMLSerializer().serializeToString(document.createTextNode(in_text)); // escape it properly?
27914         
27915         if (this.in_pre) {
27916             this.html[this.html.length] =  text;
27917             return;   
27918         }
27919         
27920         if (this.in_inline) {
27921             text = text.replace(/\s+/g,' '); // all white space inc line breaks to a slingle' '
27922             if (text != ' ') {
27923                 text = text.replace(/\s+/,' ');  // all white space to single white space
27924                 
27925                     
27926                 // if next tag is '<BR>', then we can trim right..
27927                 if (node.nextSibling &&
27928                     node.nextSibling.nodeType == 1 &&
27929                     node.nextSibling.nodeName == 'BR' )
27930                 {
27931                     text = text.replace(/\s+$/g,'');
27932                 }
27933                 // if previous tag was a BR, we can also trim..
27934                 if (node.previousSibling &&
27935                     node.previousSibling.nodeType == 1 &&
27936                     node.previousSibling.nodeName == 'BR' )
27937                 {
27938                     text = this.indentstr +  text.replace(/^\s+/g,'');
27939                 }
27940                 if (text.match(/\n/)) {
27941                     text = text.replace(
27942                         /(?![^\n]{1,64}$)([^\n]{1,64})\s/g, '$1\n' + this.indentstr
27943                     );
27944                     // remoeve the last whitespace / line break.
27945                     text = text.replace(/\n\s+$/,'');
27946                 }
27947                 // repace long lines
27948                 
27949             }
27950              
27951             this.html[this.html.length] =  text;
27952             return;   
27953         }
27954         // see if previous element was a inline element.
27955         var indentstr = this.indentstr;
27956    
27957         text = text.replace(/\s+/g," "); // all whitespace into single white space.
27958         
27959         // should trim left?
27960         if (node.previousSibling &&
27961             node.previousSibling.nodeType == 1 &&
27962             Roo.htmleditor.TidyWriter.inline_elements.indexOf(node.previousSibling.nodeName) > -1)
27963         {
27964             indentstr = '';
27965             
27966         } else {
27967             this.addLine();
27968             text = text.replace(/^\s+/,''); // trim left
27969           
27970         }
27971         // should trim right?
27972         if (node.nextSibling &&
27973             node.nextSibling.nodeType == 1 &&
27974             Roo.htmleditor.TidyWriter.inline_elements.indexOf(node.nextSibling.nodeName) > -1)
27975         {
27976           // noop
27977             
27978         }  else {
27979             text = text.replace(/\s+$/,''); // trim right
27980         }
27981          
27982               
27983         
27984         
27985         
27986         if (text.length < 1) {
27987             return;
27988         }
27989         if (!text.match(/\n/)) {
27990             this.html.push(indentstr + text);
27991             return;
27992         }
27993         
27994         text = this.indentstr + text.replace(
27995             /(?![^\n]{1,64}$)([^\n]{1,64})\s/g, '$1\n' + this.indentstr
27996         );
27997         // remoeve the last whitespace / line break.
27998         text = text.replace(/\s+$/,''); 
27999         
28000         this.html.push(text);
28001         
28002         // split and indent..
28003         
28004         
28005     },
28006     /**
28007      * Writes a cdata node such as <![CDATA[data]]>.
28008      *
28009      * @method cdata
28010      * @param {String} text String to write out inside the cdata.
28011      */
28012     cdata: function(text) {
28013         this.html.push('<![CDATA[', text, ']]>');
28014     },
28015     /**
28016     * Writes a comment node such as <!-- Comment -->.
28017     *
28018     * @method cdata
28019     * @param {String} text String to write out inside the comment.
28020     */
28021    comment: function(text) {
28022        this.html.push('<!--', text, '-->');
28023    },
28024     /**
28025      * Writes a PI node such as <?xml attr="value" ?>.
28026      *
28027      * @method pi
28028      * @param {String} name Name of the pi.
28029      * @param {String} text String to write out inside the pi.
28030      */
28031     pi: function(name, text) {
28032         text ? this.html.push('<?', name, ' ', this.encode(text), '?>') : this.html.push('<?', name, '?>');
28033         this.indent != '' && this.html.push('\n');
28034     },
28035     /**
28036      * Writes a doctype node such as <!DOCTYPE data>.
28037      *
28038      * @method doctype
28039      * @param {String} text String to write out inside the doctype.
28040      */
28041     doctype: function(text) {
28042         this.html.push('<!DOCTYPE', text, '>', this.indent != '' ? '\n' : '');
28043     },
28044     /**
28045      * Resets the internal buffer if one wants to reuse the writer.
28046      *
28047      * @method reset
28048      */
28049     reset: function() {
28050         this.html.length = 0;
28051         this.state = [];
28052         this.pushState({
28053             indentstr : '',
28054             in_pre : false, 
28055             in_inline : false
28056         })
28057     },
28058     /**
28059      * Returns the contents that got serialized.
28060      *
28061      * @method getContent
28062      * @return {String} HTML contents that got written down.
28063      */
28064     getContent: function() {
28065         return this.html.join('').replace(/\n$/, '');
28066     },
28067     
28068     pushState : function(cfg)
28069     {
28070         this.state.push(cfg);
28071         Roo.apply(this, cfg);
28072     },
28073     
28074     popState : function()
28075     {
28076         if (this.state.length < 1) {
28077             return; // nothing to push
28078         }
28079         var cfg = {
28080             in_pre: false,
28081             indentstr : ''
28082         };
28083         this.state.pop();
28084         if (this.state.length > 0) {
28085             cfg = this.state[this.state.length-1]; 
28086         }
28087         Roo.apply(this, cfg);
28088     },
28089     
28090     addLine: function()
28091     {
28092         if (this.html.length < 1) {
28093             return;
28094         }
28095         
28096         
28097         var value = this.html[this.html.length - 1];
28098         if (value.length > 0 && '\n' !== value) {
28099             this.html.push('\n');
28100         }
28101     }
28102     
28103     
28104 //'pre script noscript style textarea video audio iframe object code'
28105 // shortended... 'area base basefont br col frame hr img input isindex link  meta param embed source wbr track');
28106 // inline 
28107 };
28108
28109 Roo.htmleditor.TidyWriter.inline_elements = [
28110         'SPAN','STRONG','B','EM','I','FONT','STRIKE','U','VAR',
28111         'CITE','DFN','CODE','MARK','Q','SUP','SUB','SAMP', 'A'
28112 ];
28113 Roo.htmleditor.TidyWriter.shortend_elements = [
28114     'AREA','BASE','BASEFONT','BR','COL','FRAME','HR','IMG','INPUT',
28115     'ISINDEX','LINK','','META','PARAM','EMBED','SOURCE','WBR','TRACK'
28116 ];
28117
28118 Roo.htmleditor.TidyWriter.whitespace_elements = [
28119     'PRE','SCRIPT','NOSCRIPT','STYLE','TEXTAREA','VIDEO','AUDIO','IFRAME','OBJECT','CODE'
28120 ];/***
28121  * This is based loosely on tinymce 
28122  * @class Roo.htmleditor.TidyEntities
28123  * @static
28124  * https://github.com/thorn0/tinymce.html/blob/master/tinymce.html.js
28125  *
28126  * Not 100% sure this is actually used or needed.
28127  */
28128
28129 Roo.htmleditor.TidyEntities = {
28130     
28131     /**
28132      * initialize data..
28133      */
28134     init : function (){
28135      
28136         this.namedEntities = this.buildEntitiesLookup(this.namedEntitiesData, 32);
28137        
28138     },
28139
28140
28141     buildEntitiesLookup: function(items, radix) {
28142         var i, chr, entity, lookup = {};
28143         if (!items) {
28144             return {};
28145         }
28146         items = typeof(items) == 'string' ? items.split(',') : items;
28147         radix = radix || 10;
28148         // Build entities lookup table
28149         for (i = 0; i < items.length; i += 2) {
28150             chr = String.fromCharCode(parseInt(items[i], radix));
28151             // Only add non base entities
28152             if (!this.baseEntities[chr]) {
28153                 entity = '&' + items[i + 1] + ';';
28154                 lookup[chr] = entity;
28155                 lookup[entity] = chr;
28156             }
28157         }
28158         return lookup;
28159         
28160     },
28161     
28162     asciiMap : {
28163             128: '€',
28164             130: '‚',
28165             131: 'ƒ',
28166             132: '„',
28167             133: '…',
28168             134: '†',
28169             135: '‡',
28170             136: 'ˆ',
28171             137: '‰',
28172             138: 'Š',
28173             139: '‹',
28174             140: 'Œ',
28175             142: 'Ž',
28176             145: '‘',
28177             146: '’',
28178             147: '“',
28179             148: '”',
28180             149: '•',
28181             150: '–',
28182             151: '—',
28183             152: '˜',
28184             153: '™',
28185             154: 'š',
28186             155: '›',
28187             156: 'œ',
28188             158: 'ž',
28189             159: 'Ÿ'
28190     },
28191     // Raw entities
28192     baseEntities : {
28193         '"': '&quot;',
28194         // Needs to be escaped since the YUI compressor would otherwise break the code
28195         '\'': '&#39;',
28196         '<': '&lt;',
28197         '>': '&gt;',
28198         '&': '&amp;',
28199         '`': '&#96;'
28200     },
28201     // Reverse lookup table for raw entities
28202     reverseEntities : {
28203         '&lt;': '<',
28204         '&gt;': '>',
28205         '&amp;': '&',
28206         '&quot;': '"',
28207         '&apos;': '\''
28208     },
28209     
28210     attrsCharsRegExp : /[&<>\"\u0060\u007E-\uD7FF\uE000-\uFFEF]|[\uD800-\uDBFF][\uDC00-\uDFFF]/g,
28211     textCharsRegExp : /[<>&\u007E-\uD7FF\uE000-\uFFEF]|[\uD800-\uDBFF][\uDC00-\uDFFF]/g,
28212     rawCharsRegExp : /[<>&\"\']/g,
28213     entityRegExp : /&#([a-z0-9]+);?|&([a-z0-9]+);/gi,
28214     namedEntities  : false,
28215     namedEntitiesData : [ 
28216         '50',
28217         'nbsp',
28218         '51',
28219         'iexcl',
28220         '52',
28221         'cent',
28222         '53',
28223         'pound',
28224         '54',
28225         'curren',
28226         '55',
28227         'yen',
28228         '56',
28229         'brvbar',
28230         '57',
28231         'sect',
28232         '58',
28233         'uml',
28234         '59',
28235         'copy',
28236         '5a',
28237         'ordf',
28238         '5b',
28239         'laquo',
28240         '5c',
28241         'not',
28242         '5d',
28243         'shy',
28244         '5e',
28245         'reg',
28246         '5f',
28247         'macr',
28248         '5g',
28249         'deg',
28250         '5h',
28251         'plusmn',
28252         '5i',
28253         'sup2',
28254         '5j',
28255         'sup3',
28256         '5k',
28257         'acute',
28258         '5l',
28259         'micro',
28260         '5m',
28261         'para',
28262         '5n',
28263         'middot',
28264         '5o',
28265         'cedil',
28266         '5p',
28267         'sup1',
28268         '5q',
28269         'ordm',
28270         '5r',
28271         'raquo',
28272         '5s',
28273         'frac14',
28274         '5t',
28275         'frac12',
28276         '5u',
28277         'frac34',
28278         '5v',
28279         'iquest',
28280         '60',
28281         'Agrave',
28282         '61',
28283         'Aacute',
28284         '62',
28285         'Acirc',
28286         '63',
28287         'Atilde',
28288         '64',
28289         'Auml',
28290         '65',
28291         'Aring',
28292         '66',
28293         'AElig',
28294         '67',
28295         'Ccedil',
28296         '68',
28297         'Egrave',
28298         '69',
28299         'Eacute',
28300         '6a',
28301         'Ecirc',
28302         '6b',
28303         'Euml',
28304         '6c',
28305         'Igrave',
28306         '6d',
28307         'Iacute',
28308         '6e',
28309         'Icirc',
28310         '6f',
28311         'Iuml',
28312         '6g',
28313         'ETH',
28314         '6h',
28315         'Ntilde',
28316         '6i',
28317         'Ograve',
28318         '6j',
28319         'Oacute',
28320         '6k',
28321         'Ocirc',
28322         '6l',
28323         'Otilde',
28324         '6m',
28325         'Ouml',
28326         '6n',
28327         'times',
28328         '6o',
28329         'Oslash',
28330         '6p',
28331         'Ugrave',
28332         '6q',
28333         'Uacute',
28334         '6r',
28335         'Ucirc',
28336         '6s',
28337         'Uuml',
28338         '6t',
28339         'Yacute',
28340         '6u',
28341         'THORN',
28342         '6v',
28343         'szlig',
28344         '70',
28345         'agrave',
28346         '71',
28347         'aacute',
28348         '72',
28349         'acirc',
28350         '73',
28351         'atilde',
28352         '74',
28353         'auml',
28354         '75',
28355         'aring',
28356         '76',
28357         'aelig',
28358         '77',
28359         'ccedil',
28360         '78',
28361         'egrave',
28362         '79',
28363         'eacute',
28364         '7a',
28365         'ecirc',
28366         '7b',
28367         'euml',
28368         '7c',
28369         'igrave',
28370         '7d',
28371         'iacute',
28372         '7e',
28373         'icirc',
28374         '7f',
28375         'iuml',
28376         '7g',
28377         'eth',
28378         '7h',
28379         'ntilde',
28380         '7i',
28381         'ograve',
28382         '7j',
28383         'oacute',
28384         '7k',
28385         'ocirc',
28386         '7l',
28387         'otilde',
28388         '7m',
28389         'ouml',
28390         '7n',
28391         'divide',
28392         '7o',
28393         'oslash',
28394         '7p',
28395         'ugrave',
28396         '7q',
28397         'uacute',
28398         '7r',
28399         'ucirc',
28400         '7s',
28401         'uuml',
28402         '7t',
28403         'yacute',
28404         '7u',
28405         'thorn',
28406         '7v',
28407         'yuml',
28408         'ci',
28409         'fnof',
28410         'sh',
28411         'Alpha',
28412         'si',
28413         'Beta',
28414         'sj',
28415         'Gamma',
28416         'sk',
28417         'Delta',
28418         'sl',
28419         'Epsilon',
28420         'sm',
28421         'Zeta',
28422         'sn',
28423         'Eta',
28424         'so',
28425         'Theta',
28426         'sp',
28427         'Iota',
28428         'sq',
28429         'Kappa',
28430         'sr',
28431         'Lambda',
28432         'ss',
28433         'Mu',
28434         'st',
28435         'Nu',
28436         'su',
28437         'Xi',
28438         'sv',
28439         'Omicron',
28440         't0',
28441         'Pi',
28442         't1',
28443         'Rho',
28444         't3',
28445         'Sigma',
28446         't4',
28447         'Tau',
28448         't5',
28449         'Upsilon',
28450         't6',
28451         'Phi',
28452         't7',
28453         'Chi',
28454         't8',
28455         'Psi',
28456         't9',
28457         'Omega',
28458         'th',
28459         'alpha',
28460         'ti',
28461         'beta',
28462         'tj',
28463         'gamma',
28464         'tk',
28465         'delta',
28466         'tl',
28467         'epsilon',
28468         'tm',
28469         'zeta',
28470         'tn',
28471         'eta',
28472         'to',
28473         'theta',
28474         'tp',
28475         'iota',
28476         'tq',
28477         'kappa',
28478         'tr',
28479         'lambda',
28480         'ts',
28481         'mu',
28482         'tt',
28483         'nu',
28484         'tu',
28485         'xi',
28486         'tv',
28487         'omicron',
28488         'u0',
28489         'pi',
28490         'u1',
28491         'rho',
28492         'u2',
28493         'sigmaf',
28494         'u3',
28495         'sigma',
28496         'u4',
28497         'tau',
28498         'u5',
28499         'upsilon',
28500         'u6',
28501         'phi',
28502         'u7',
28503         'chi',
28504         'u8',
28505         'psi',
28506         'u9',
28507         'omega',
28508         'uh',
28509         'thetasym',
28510         'ui',
28511         'upsih',
28512         'um',
28513         'piv',
28514         '812',
28515         'bull',
28516         '816',
28517         'hellip',
28518         '81i',
28519         'prime',
28520         '81j',
28521         'Prime',
28522         '81u',
28523         'oline',
28524         '824',
28525         'frasl',
28526         '88o',
28527         'weierp',
28528         '88h',
28529         'image',
28530         '88s',
28531         'real',
28532         '892',
28533         'trade',
28534         '89l',
28535         'alefsym',
28536         '8cg',
28537         'larr',
28538         '8ch',
28539         'uarr',
28540         '8ci',
28541         'rarr',
28542         '8cj',
28543         'darr',
28544         '8ck',
28545         'harr',
28546         '8dl',
28547         'crarr',
28548         '8eg',
28549         'lArr',
28550         '8eh',
28551         'uArr',
28552         '8ei',
28553         'rArr',
28554         '8ej',
28555         'dArr',
28556         '8ek',
28557         'hArr',
28558         '8g0',
28559         'forall',
28560         '8g2',
28561         'part',
28562         '8g3',
28563         'exist',
28564         '8g5',
28565         'empty',
28566         '8g7',
28567         'nabla',
28568         '8g8',
28569         'isin',
28570         '8g9',
28571         'notin',
28572         '8gb',
28573         'ni',
28574         '8gf',
28575         'prod',
28576         '8gh',
28577         'sum',
28578         '8gi',
28579         'minus',
28580         '8gn',
28581         'lowast',
28582         '8gq',
28583         'radic',
28584         '8gt',
28585         'prop',
28586         '8gu',
28587         'infin',
28588         '8h0',
28589         'ang',
28590         '8h7',
28591         'and',
28592         '8h8',
28593         'or',
28594         '8h9',
28595         'cap',
28596         '8ha',
28597         'cup',
28598         '8hb',
28599         'int',
28600         '8hk',
28601         'there4',
28602         '8hs',
28603         'sim',
28604         '8i5',
28605         'cong',
28606         '8i8',
28607         'asymp',
28608         '8j0',
28609         'ne',
28610         '8j1',
28611         'equiv',
28612         '8j4',
28613         'le',
28614         '8j5',
28615         'ge',
28616         '8k2',
28617         'sub',
28618         '8k3',
28619         'sup',
28620         '8k4',
28621         'nsub',
28622         '8k6',
28623         'sube',
28624         '8k7',
28625         'supe',
28626         '8kl',
28627         'oplus',
28628         '8kn',
28629         'otimes',
28630         '8l5',
28631         'perp',
28632         '8m5',
28633         'sdot',
28634         '8o8',
28635         'lceil',
28636         '8o9',
28637         'rceil',
28638         '8oa',
28639         'lfloor',
28640         '8ob',
28641         'rfloor',
28642         '8p9',
28643         'lang',
28644         '8pa',
28645         'rang',
28646         '9ea',
28647         'loz',
28648         '9j0',
28649         'spades',
28650         '9j3',
28651         'clubs',
28652         '9j5',
28653         'hearts',
28654         '9j6',
28655         'diams',
28656         'ai',
28657         'OElig',
28658         'aj',
28659         'oelig',
28660         'b0',
28661         'Scaron',
28662         'b1',
28663         'scaron',
28664         'bo',
28665         'Yuml',
28666         'm6',
28667         'circ',
28668         'ms',
28669         'tilde',
28670         '802',
28671         'ensp',
28672         '803',
28673         'emsp',
28674         '809',
28675         'thinsp',
28676         '80c',
28677         'zwnj',
28678         '80d',
28679         'zwj',
28680         '80e',
28681         'lrm',
28682         '80f',
28683         'rlm',
28684         '80j',
28685         'ndash',
28686         '80k',
28687         'mdash',
28688         '80o',
28689         'lsquo',
28690         '80p',
28691         'rsquo',
28692         '80q',
28693         'sbquo',
28694         '80s',
28695         'ldquo',
28696         '80t',
28697         'rdquo',
28698         '80u',
28699         'bdquo',
28700         '810',
28701         'dagger',
28702         '811',
28703         'Dagger',
28704         '81g',
28705         'permil',
28706         '81p',
28707         'lsaquo',
28708         '81q',
28709         'rsaquo',
28710         '85c',
28711         'euro'
28712     ],
28713
28714          
28715     /**
28716      * Encodes the specified string using raw entities. This means only the required XML base entities will be encoded.
28717      *
28718      * @method encodeRaw
28719      * @param {String} text Text to encode.
28720      * @param {Boolean} attr Optional flag to specify if the text is attribute contents.
28721      * @return {String} Entity encoded text.
28722      */
28723     encodeRaw: function(text, attr)
28724     {
28725         var t = this;
28726         return text.replace(attr ? this.attrsCharsRegExp : this.textCharsRegExp, function(chr) {
28727             return t.baseEntities[chr] || chr;
28728         });
28729     },
28730     /**
28731      * Encoded the specified text with both the attributes and text entities. This function will produce larger text contents
28732      * since it doesn't know if the context is within a attribute or text node. This was added for compatibility
28733      * and is exposed as the DOMUtils.encode function.
28734      *
28735      * @method encodeAllRaw
28736      * @param {String} text Text to encode.
28737      * @return {String} Entity encoded text.
28738      */
28739     encodeAllRaw: function(text) {
28740         var t = this;
28741         return ('' + text).replace(this.rawCharsRegExp, function(chr) {
28742             return t.baseEntities[chr] || chr;
28743         });
28744     },
28745     /**
28746      * Encodes the specified string using numeric entities. The core entities will be
28747      * encoded as named ones but all non lower ascii characters will be encoded into numeric entities.
28748      *
28749      * @method encodeNumeric
28750      * @param {String} text Text to encode.
28751      * @param {Boolean} attr Optional flag to specify if the text is attribute contents.
28752      * @return {String} Entity encoded text.
28753      */
28754     encodeNumeric: function(text, attr) {
28755         var t = this;
28756         return text.replace(attr ? this.attrsCharsRegExp : this.textCharsRegExp, function(chr) {
28757             // Multi byte sequence convert it to a single entity
28758             if (chr.length > 1) {
28759                 return '&#' + (1024 * (chr.charCodeAt(0) - 55296) + (chr.charCodeAt(1) - 56320) + 65536) + ';';
28760             }
28761             return t.baseEntities[chr] || '&#' + chr.charCodeAt(0) + ';';
28762         });
28763     },
28764     /**
28765      * Encodes the specified string using named entities. The core entities will be encoded
28766      * as named ones but all non lower ascii characters will be encoded into named entities.
28767      *
28768      * @method encodeNamed
28769      * @param {String} text Text to encode.
28770      * @param {Boolean} attr Optional flag to specify if the text is attribute contents.
28771      * @param {Object} entities Optional parameter with entities to use.
28772      * @return {String} Entity encoded text.
28773      */
28774     encodeNamed: function(text, attr, entities) {
28775         var t = this;
28776         entities = entities || this.namedEntities;
28777         return text.replace(attr ? this.attrsCharsRegExp : this.textCharsRegExp, function(chr) {
28778             return t.baseEntities[chr] || entities[chr] || chr;
28779         });
28780     },
28781     /**
28782      * Returns an encode function based on the name(s) and it's optional entities.
28783      *
28784      * @method getEncodeFunc
28785      * @param {String} name Comma separated list of encoders for example named,numeric.
28786      * @param {String} entities Optional parameter with entities to use instead of the built in set.
28787      * @return {function} Encode function to be used.
28788      */
28789     getEncodeFunc: function(name, entities) {
28790         entities = this.buildEntitiesLookup(entities) || this.namedEntities;
28791         var t = this;
28792         function encodeNamedAndNumeric(text, attr) {
28793             return text.replace(attr ? t.attrsCharsRegExp : t.textCharsRegExp, function(chr) {
28794                 return t.baseEntities[chr] || entities[chr] || '&#' + chr.charCodeAt(0) + ';' || chr;
28795             });
28796         }
28797
28798         function encodeCustomNamed(text, attr) {
28799             return t.encodeNamed(text, attr, entities);
28800         }
28801         // Replace + with , to be compatible with previous TinyMCE versions
28802         name = this.makeMap(name.replace(/\+/g, ','));
28803         // Named and numeric encoder
28804         if (name.named && name.numeric) {
28805             return this.encodeNamedAndNumeric;
28806         }
28807         // Named encoder
28808         if (name.named) {
28809             // Custom names
28810             if (entities) {
28811                 return encodeCustomNamed;
28812             }
28813             return this.encodeNamed;
28814         }
28815         // Numeric
28816         if (name.numeric) {
28817             return this.encodeNumeric;
28818         }
28819         // Raw encoder
28820         return this.encodeRaw;
28821     },
28822     /**
28823      * Decodes the specified string, this will replace entities with raw UTF characters.
28824      *
28825      * @method decode
28826      * @param {String} text Text to entity decode.
28827      * @return {String} Entity decoded string.
28828      */
28829     decode: function(text)
28830     {
28831         var  t = this;
28832         return text.replace(this.entityRegExp, function(all, numeric) {
28833             if (numeric) {
28834                 numeric = 'x' === numeric.charAt(0).toLowerCase() ? parseInt(numeric.substr(1), 16) : parseInt(numeric, 10);
28835                 // Support upper UTF
28836                 if (numeric > 65535) {
28837                     numeric -= 65536;
28838                     return String.fromCharCode(55296 + (numeric >> 10), 56320 + (1023 & numeric));
28839                 }
28840                 return t.asciiMap[numeric] || String.fromCharCode(numeric);
28841             }
28842             return t.reverseEntities[all] || t.namedEntities[all] || t.nativeDecode(all);
28843         });
28844     },
28845     nativeDecode : function (text) {
28846         return text;
28847     },
28848     makeMap : function (items, delim, map) {
28849                 var i;
28850                 items = items || [];
28851                 delim = delim || ',';
28852                 if (typeof items == "string") {
28853                         items = items.split(delim);
28854                 }
28855                 map = map || {};
28856                 i = items.length;
28857                 while (i--) {
28858                         map[items[i]] = {};
28859                 }
28860                 return map;
28861         }
28862 };
28863     
28864     
28865     
28866 Roo.htmleditor.TidyEntities.init();
28867 /**
28868  * @class Roo.htmleditor.KeyEnter
28869  * Handle Enter press..
28870  * @cfg {Roo.HtmlEditorCore} core the editor.
28871  * @constructor
28872  * Create a new Filter.
28873  * @param {Object} config Configuration options
28874  */
28875
28876
28877
28878
28879
28880 Roo.htmleditor.KeyEnter = function(cfg) {
28881     Roo.apply(this, cfg);
28882     // this does not actually call walk as it's really just a abstract class
28883  
28884     Roo.get(this.core.doc.body).on('keypress', this.keypress, this);
28885 }
28886
28887 //Roo.htmleditor.KeyEnter.i = 0;
28888
28889
28890 Roo.htmleditor.KeyEnter.prototype = {
28891     
28892     core : false,
28893     
28894     keypress : function(e)
28895     {
28896         if (e.charCode != 13 && e.charCode != 10) {
28897             Roo.log([e.charCode,e]);
28898             return true;
28899         }
28900         e.preventDefault();
28901         // https://stackoverflow.com/questions/18552336/prevent-contenteditable-adding-div-on-enter-chrome
28902         var doc = this.core.doc;
28903           //add a new line
28904        
28905     
28906         var sel = this.core.getSelection();
28907         var range = sel.getRangeAt(0);
28908         var n = range.commonAncestorContainer;
28909         var pc = range.closest([ 'ol', 'ul']);
28910         var pli = range.closest('li');
28911         if (!pc || e.ctrlKey) {
28912             // on it list, or ctrl pressed.
28913             if (!e.ctrlKey) {
28914                 sel.insertNode('br', 'after'); 
28915             } else {
28916                 // only do this if we have ctrl key..
28917                 var br = doc.createElement('br');
28918                 br.className = 'clear';
28919                 br.setAttribute('style', 'clear: both');
28920                 sel.insertNode(br, 'after'); 
28921             }
28922             
28923          
28924             this.core.undoManager.addEvent();
28925             this.core.fireEditorEvent(e);
28926             return false;
28927         }
28928         
28929         // deal with <li> insetion
28930         if (pli.innerText.trim() == '' &&
28931             pli.previousSibling &&
28932             pli.previousSibling.nodeName == 'LI' &&
28933             pli.previousSibling.innerText.trim() ==  '') {
28934             pli.parentNode.removeChild(pli.previousSibling);
28935             sel.cursorAfter(pc);
28936             this.core.undoManager.addEvent();
28937             this.core.fireEditorEvent(e);
28938             return false;
28939         }
28940     
28941         var li = doc.createElement('LI');
28942         li.innerHTML = '&nbsp;';
28943         if (!pli || !pli.firstSibling) {
28944             pc.appendChild(li);
28945         } else {
28946             pli.parentNode.insertBefore(li, pli.firstSibling);
28947         }
28948         sel.cursorText (li.firstChild);
28949       
28950         this.core.undoManager.addEvent();
28951         this.core.fireEditorEvent(e);
28952
28953         return false;
28954         
28955     
28956         
28957         
28958          
28959     }
28960 };
28961      
28962 /**
28963  * @class Roo.htmleditor.Block
28964  * Base class for html editor blocks - do not use it directly .. extend it..
28965  * @cfg {DomElement} node The node to apply stuff to.
28966  * @cfg {String} friendly_name the name that appears in the context bar about this block
28967  * @cfg {Object} Context menu - see Roo.form.HtmlEditor.ToolbarContext
28968  
28969  * @constructor
28970  * Create a new Filter.
28971  * @param {Object} config Configuration options
28972  */
28973
28974 Roo.htmleditor.Block  = function(cfg)
28975 {
28976     // do nothing .. should not be called really.
28977 }
28978 /**
28979  * factory method to get the block from an element (using cache if necessary)
28980  * @static
28981  * @param {HtmlElement} the dom element
28982  */
28983 Roo.htmleditor.Block.factory = function(node)
28984 {
28985     var cc = Roo.htmleditor.Block.cache;
28986     var id = Roo.get(node).id;
28987     if (typeof(cc[id]) != 'undefined' && (!cc[id].node || cc[id].node.closest('body'))) {
28988         Roo.htmleditor.Block.cache[id].readElement(node);
28989         return Roo.htmleditor.Block.cache[id];
28990     }
28991     var db  = node.getAttribute('data-block');
28992     if (!db) {
28993         db = node.nodeName.toLowerCase().toUpperCaseFirst();
28994     }
28995     var cls = Roo.htmleditor['Block' + db];
28996     if (typeof(cls) == 'undefined') {
28997         //Roo.log(node.getAttribute('data-block'));
28998         Roo.log("OOps missing block : " + 'Block' + db);
28999         return false;
29000     }
29001     Roo.htmleditor.Block.cache[id] = new cls({ node: node });
29002     return Roo.htmleditor.Block.cache[id];  /// should trigger update element
29003 };
29004
29005 /**
29006  * initalize all Elements from content that are 'blockable'
29007  * @static
29008  * @param the body element
29009  */
29010 Roo.htmleditor.Block.initAll = function(body, type)
29011 {
29012     if (typeof(type) == 'undefined') {
29013         var ia = Roo.htmleditor.Block.initAll;
29014         ia(body,'table');
29015         ia(body,'td');
29016         ia(body,'figure');
29017         return;
29018     }
29019     Roo.each(Roo.get(body).query(type), function(e) {
29020         Roo.htmleditor.Block.factory(e);    
29021     },this);
29022 };
29023 // question goes here... do we need to clear out this cache sometimes?
29024 // or show we make it relivant to the htmleditor.
29025 Roo.htmleditor.Block.cache = {};
29026
29027 Roo.htmleditor.Block.prototype = {
29028     
29029     node : false,
29030     
29031      // used by context menu
29032     friendly_name : 'Based Block',
29033     
29034     // text for button to delete this element
29035     deleteTitle : false,
29036     
29037     context : false,
29038     /**
29039      * Update a node with values from this object
29040      * @param {DomElement} node
29041      */
29042     updateElement : function(node)
29043     {
29044         Roo.DomHelper.update(node === undefined ? this.node : node, this.toObject());
29045     },
29046      /**
29047      * convert to plain HTML for calling insertAtCursor..
29048      */
29049     toHTML : function()
29050     {
29051         return Roo.DomHelper.markup(this.toObject());
29052     },
29053     /**
29054      * used by readEleemnt to extract data from a node
29055      * may need improving as it's pretty basic
29056      
29057      * @param {DomElement} node
29058      * @param {String} tag - tag to find, eg. IMG ?? might be better to use DomQuery ?
29059      * @param {String} attribute (use html - for contents, style for using next param as style, or false to return the node)
29060      * @param {String} style the style property - eg. text-align
29061      */
29062     getVal : function(node, tag, attr, style)
29063     {
29064         var n = node;
29065         if (tag !== true && n.tagName != tag.toUpperCase()) {
29066             // in theory we could do figure[3] << 3rd figure? or some more complex search..?
29067             // but kiss for now.
29068             n = node.getElementsByTagName(tag).item(0);
29069         }
29070         if (!n) {
29071             return '';
29072         }
29073         if (attr === false) {
29074             return n;
29075         }
29076         if (attr == 'html') {
29077             return n.innerHTML;
29078         }
29079         if (attr == 'style') {
29080             return n.style[style]; 
29081         }
29082         
29083         return n.hasAttribute(attr) ? n.getAttribute(attr) : '';
29084             
29085     },
29086     /**
29087      * create a DomHelper friendly object - for use with 
29088      * Roo.DomHelper.markup / overwrite / etc..
29089      * (override this)
29090      */
29091     toObject : function()
29092     {
29093         return {};
29094     },
29095       /**
29096      * Read a node that has a 'data-block' property - and extract the values from it.
29097      * @param {DomElement} node - the node
29098      */
29099     readElement : function(node)
29100     {
29101         
29102     } 
29103     
29104     
29105 };
29106
29107  
29108
29109 /**
29110  * @class Roo.htmleditor.BlockFigure
29111  * Block that has an image and a figcaption
29112  * @cfg {String} image_src the url for the image
29113  * @cfg {String} align (left|right) alignment for the block default left
29114  * @cfg {String} caption the text to appear below  (and in the alt tag)
29115  * @cfg {String} caption_display (block|none) display or not the caption
29116  * @cfg {String|number} image_width the width of the image number or %?
29117  * @cfg {String|number} image_height the height of the image number or %?
29118  * 
29119  * @constructor
29120  * Create a new Filter.
29121  * @param {Object} config Configuration options
29122  */
29123
29124 Roo.htmleditor.BlockFigure = function(cfg)
29125 {
29126     if (cfg.node) {
29127         this.readElement(cfg.node);
29128         this.updateElement(cfg.node);
29129     }
29130     Roo.apply(this, cfg);
29131 }
29132 Roo.extend(Roo.htmleditor.BlockFigure, Roo.htmleditor.Block, {
29133  
29134     
29135     // setable values.
29136     image_src: '',
29137     align: 'center',
29138     caption : '',
29139     caption_display : 'block',
29140     width : '100%',
29141     cls : '',
29142     href: '',
29143     video_url : '',
29144     
29145     // margin: '2%', not used
29146     
29147     text_align: 'left', //   (left|right) alignment for the text caption default left. - not used at present
29148
29149     
29150     // used by context menu
29151     friendly_name : 'Image with caption',
29152     deleteTitle : "Delete Image and Caption",
29153     
29154     contextMenu : function(toolbar)
29155     {
29156         
29157         var block = function() {
29158             return Roo.htmleditor.Block.factory(toolbar.tb.selectedNode);
29159         };
29160         
29161         
29162         var rooui =  typeof(Roo.bootstrap) == 'undefined' ? Roo : Roo.bootstrap;
29163         
29164         var syncValue = toolbar.editorcore.syncValue;
29165         
29166         var fields = {};
29167         
29168         return [
29169              {
29170                 xtype : 'TextItem',
29171                 text : "Source: ",
29172                 xns : rooui.Toolbar  //Boostrap?
29173             },
29174             {
29175                 xtype : 'Button',
29176                 text: 'Change Image URL',
29177                  
29178                 listeners : {
29179                     click: function (btn, state)
29180                     {
29181                         var b = block();
29182                         
29183                         Roo.MessageBox.show({
29184                             title : "Image Source URL",
29185                             msg : "Enter the url for the image",
29186                             buttons: Roo.MessageBox.OKCANCEL,
29187                             fn: function(btn, val){
29188                                 if (btn != 'ok') {
29189                                     return;
29190                                 }
29191                                 b.image_src = val;
29192                                 b.updateElement();
29193                                 syncValue();
29194                                 toolbar.editorcore.onEditorEvent();
29195                             },
29196                             minWidth:250,
29197                             prompt:true,
29198                             //multiline: multiline,
29199                             modal : true,
29200                             value : b.image_src
29201                         });
29202                     }
29203                 },
29204                 xns : rooui.Toolbar
29205             },
29206          
29207             {
29208                 xtype : 'Button',
29209                 text: 'Change Link URL',
29210                  
29211                 listeners : {
29212                     click: function (btn, state)
29213                     {
29214                         var b = block();
29215                         
29216                         Roo.MessageBox.show({
29217                             title : "Link URL",
29218                             msg : "Enter the url for the link - leave blank to have no link",
29219                             buttons: Roo.MessageBox.OKCANCEL,
29220                             fn: function(btn, val){
29221                                 if (btn != 'ok') {
29222                                     return;
29223                                 }
29224                                 b.href = val;
29225                                 b.updateElement();
29226                                 syncValue();
29227                                 toolbar.editorcore.onEditorEvent();
29228                             },
29229                             minWidth:250,
29230                             prompt:true,
29231                             //multiline: multiline,
29232                             modal : true,
29233                             value : b.href
29234                         });
29235                     }
29236                 },
29237                 xns : rooui.Toolbar
29238             },
29239             {
29240                 xtype : 'Button',
29241                 text: 'Show Video URL',
29242                  
29243                 listeners : {
29244                     click: function (btn, state)
29245                     {
29246                         Roo.MessageBox.alert("Video URL",
29247                             block().video_url == '' ? 'This image is not linked ot a video' :
29248                                 'The image is linked to: <a target="_new" href="' + block().video_url + '">' + block().video_url + '</a>');
29249                     }
29250                 },
29251                 xns : rooui.Toolbar
29252             },
29253             
29254             
29255             {
29256                 xtype : 'TextItem',
29257                 text : "Width: ",
29258                 xns : rooui.Toolbar  //Boostrap?
29259             },
29260             {
29261                 xtype : 'ComboBox',
29262                 allowBlank : false,
29263                 displayField : 'val',
29264                 editable : true,
29265                 listWidth : 100,
29266                 triggerAction : 'all',
29267                 typeAhead : true,
29268                 valueField : 'val',
29269                 width : 70,
29270                 name : 'width',
29271                 listeners : {
29272                     select : function (combo, r, index)
29273                     {
29274                         toolbar.editorcore.selectNode(toolbar.tb.selectedNode);
29275                         var b = block();
29276                         b.width = r.get('val');
29277                         b.updateElement();
29278                         syncValue();
29279                         toolbar.editorcore.onEditorEvent();
29280                     }
29281                 },
29282                 xns : rooui.form,
29283                 store : {
29284                     xtype : 'SimpleStore',
29285                     data : [
29286                         ['100%'],
29287                         ['80%'],
29288                         ['50%'],
29289                         ['20%'],
29290                         ['10%']
29291                     ],
29292                     fields : [ 'val'],
29293                     xns : Roo.data
29294                 }
29295             },
29296             {
29297                 xtype : 'TextItem',
29298                 text : "Align: ",
29299                 xns : rooui.Toolbar  //Boostrap?
29300             },
29301             {
29302                 xtype : 'ComboBox',
29303                 allowBlank : false,
29304                 displayField : 'val',
29305                 editable : true,
29306                 listWidth : 100,
29307                 triggerAction : 'all',
29308                 typeAhead : true,
29309                 valueField : 'val',
29310                 width : 70,
29311                 name : 'align',
29312                 listeners : {
29313                     select : function (combo, r, index)
29314                     {
29315                         toolbar.editorcore.selectNode(toolbar.tb.selectedNode);
29316                         var b = block();
29317                         b.align = r.get('val');
29318                         b.updateElement();
29319                         syncValue();
29320                         toolbar.editorcore.onEditorEvent();
29321                     }
29322                 },
29323                 xns : rooui.form,
29324                 store : {
29325                     xtype : 'SimpleStore',
29326                     data : [
29327                         ['left'],
29328                         ['right'],
29329                         ['center']
29330                     ],
29331                     fields : [ 'val'],
29332                     xns : Roo.data
29333                 }
29334             },
29335             
29336               
29337             {
29338                 xtype : 'Button',
29339                 text: 'Hide Caption',
29340                 name : 'caption_display',
29341                 pressed : false,
29342                 enableToggle : true,
29343                 setValue : function(v) {
29344                     // this trigger toggle.
29345                      
29346                     this.setText(v ? "Hide Caption" : "Show Caption");
29347                     this.setPressed(v != 'block');
29348                 },
29349                 listeners : {
29350                     toggle: function (btn, state)
29351                     {
29352                         var b  = block();
29353                         b.caption_display = b.caption_display == 'block' ? 'none' : 'block';
29354                         this.setText(b.caption_display == 'block' ? "Hide Caption" : "Show Caption");
29355                         b.updateElement();
29356                         syncValue();
29357                         toolbar.editorcore.selectNode(toolbar.tb.selectedNode);
29358                         toolbar.editorcore.onEditorEvent();
29359                     }
29360                 },
29361                 xns : rooui.Toolbar
29362             }
29363         ];
29364         
29365     },
29366     /**
29367      * create a DomHelper friendly object - for use with
29368      * Roo.DomHelper.markup / overwrite / etc..
29369      */
29370     toObject : function()
29371     {
29372         var d = document.createElement('div');
29373         d.innerHTML = this.caption;
29374         
29375         var m = this.width != '100%' && this.align == 'center' ? '0 auto' : 0; 
29376         
29377         var iw = this.align == 'center' ? this.width : '100%';
29378         var img =   {
29379             tag : 'img',
29380             contenteditable : 'false',
29381             src : this.image_src,
29382             alt : d.innerText.replace(/\n/g, " ").replace(/\s+/g, ' ').trim(), // removeHTML and reduce spaces..
29383             style: {
29384                 width : iw,
29385                 maxWidth : iw + ' !important', // this is not getting rendered?
29386                 margin : m  
29387                 
29388             }
29389         };
29390         /*
29391         '<div class="{0}" width="420" height="315" src="{1}" frameborder="0" allowfullscreen>' +
29392                     '<a href="{2}">' + 
29393                         '<img class="{0}-thumbnail" src="{3}/Images/{4}/{5}#image-{4}" />' + 
29394                     '</a>' + 
29395                 '</div>',
29396         */
29397                 
29398         if (this.href.length > 0) {
29399             img = {
29400                 tag : 'a',
29401                 href: this.href,
29402                 contenteditable : 'true',
29403                 cn : [
29404                     img
29405                 ]
29406             };
29407         }
29408         
29409         
29410         if (this.video_url.length > 0) {
29411             img = {
29412                 tag : 'div',
29413                 cls : this.cls,
29414                 frameborder : 0,
29415                 allowfullscreen : true,
29416                 width : 420,  // these are for video tricks - that we replace the outer
29417                 height : 315,
29418                 src : this.video_url,
29419                 cn : [
29420                     img
29421                 ]
29422             };
29423         }
29424         // we remove caption totally if its hidden... - will delete data.. but otherwise we end up with fake caption
29425         var captionhtml = this.caption_display == 'none' ? '' : (this.caption.length ? this.caption : "Caption");
29426         
29427   
29428         var ret =   {
29429             tag: 'figure',
29430             'data-block' : 'Figure',
29431             'data-width' : this.width,
29432             'data-caption' : this.caption, 
29433             contenteditable : 'false',
29434             
29435             style : {
29436                 display: 'block',
29437                 float :  this.align ,
29438                 maxWidth :  this.align == 'center' ? '100% !important' : (this.width + ' !important'),
29439                 width : this.align == 'center' ? '100%' : this.width,
29440                 margin:  '0px',
29441                 padding: this.align == 'center' ? '0' : '0 10px' ,
29442                 textAlign : this.align   // seems to work for email..
29443                 
29444             },
29445            
29446             
29447             align : this.align,
29448             cn : [
29449                 img,
29450               
29451                 {
29452                     tag: 'figcaption',
29453                     'data-display' : this.caption_display,
29454                     style : {
29455                         textAlign : 'left',
29456                         fontSize : '16px',
29457                         lineHeight : '24px',
29458                         display : this.caption_display,
29459                         maxWidth : (this.align == 'center' ?  this.width : '100%' ) + ' !important',
29460                         margin: m,
29461                         width: this.align == 'center' ?  this.width : '100%' 
29462                     
29463                          
29464                     },
29465                     cls : this.cls.length > 0 ? (this.cls  + '-thumbnail' ) : '',
29466                     cn : [
29467                         {
29468                             tag: 'div',
29469                             style  : {
29470                                 marginTop : '16px',
29471                                 textAlign : 'left'
29472                             },
29473                             align: 'left',
29474                             cn : [
29475                                 {
29476                                     // we can not rely on yahoo syndication to use CSS elements - so have to use  '<i>' to encase stuff.
29477                                     tag : 'i',
29478                                     contenteditable : true,
29479                                     html : captionhtml
29480                                 }
29481                                 
29482                             ]
29483                         }
29484                         
29485                     ]
29486                     
29487                 }
29488             ]
29489         };
29490         return ret;
29491          
29492     },
29493     
29494     readElement : function(node)
29495     {
29496         // this should not really come from the link...
29497         this.video_url = this.getVal(node, 'div', 'src');
29498         this.cls = this.getVal(node, 'div', 'class');
29499         this.href = this.getVal(node, 'a', 'href');
29500         
29501         
29502         this.image_src = this.getVal(node, 'img', 'src');
29503          
29504         this.align = this.getVal(node, 'figure', 'align');
29505         
29506         /// not really used - as hidden captions do not store the content here..
29507         var figcaption = this.getVal(node, 'figcaption', false);
29508         if (figcaption !== '') {
29509             this.caption = this.getVal(figcaption, 'i', 'html');
29510         }
29511         
29512
29513         this.caption_display = this.getVal(node, 'figcaption', 'data-display');
29514         var dc = this.getVal(node, true, 'data-caption');
29515         if (dc && dc.length) {
29516             this.caption = dc;
29517         }
29518         //this.text_align = this.getVal(node, 'figcaption', 'style','text-align');
29519         this.width = this.getVal(node, true, 'data-width');
29520         //this.margin = this.getVal(node, 'figure', 'style', 'margin');
29521         
29522     },
29523     removeNode : function()
29524     {
29525         return this.node;
29526     }
29527     
29528   
29529    
29530      
29531     
29532     
29533     
29534     
29535 })
29536
29537  
29538
29539 /**
29540  * @class Roo.htmleditor.BlockTable
29541  * Block that manages a table
29542  * 
29543  * @constructor
29544  * Create a new Filter.
29545  * @param {Object} config Configuration options
29546  */
29547
29548 Roo.htmleditor.BlockTable = function(cfg)
29549 {
29550     if (cfg.node) {
29551         this.readElement(cfg.node);
29552         this.updateElement(cfg.node);
29553     }
29554     Roo.apply(this, cfg);
29555     if (!cfg.node) {
29556         this.rows = [];
29557         for(var r = 0; r < this.no_row; r++) {
29558             this.rows[r] = [];
29559             for(var c = 0; c < this.no_col; c++) {
29560                 this.rows[r][c] = this.emptyCell();
29561             }
29562         }
29563     }
29564     
29565     
29566 }
29567 Roo.extend(Roo.htmleditor.BlockTable, Roo.htmleditor.Block, {
29568  
29569     rows : false,
29570     no_col : 1,
29571     no_row : 1,
29572     
29573     
29574     width: '100%',
29575     
29576     // used by context menu
29577     friendly_name : 'Table',
29578     deleteTitle : 'Delete Table',
29579     // context menu is drawn once..
29580     
29581     contextMenu : function(toolbar)
29582     {
29583         
29584         var block = function() {
29585             return Roo.htmleditor.Block.factory(toolbar.tb.selectedNode);
29586         };
29587         
29588         
29589         var rooui =  typeof(Roo.bootstrap) == 'undefined' ? Roo : Roo.bootstrap;
29590         
29591         var syncValue = toolbar.editorcore.syncValue;
29592         
29593         var fields = {};
29594         
29595         return [
29596             {
29597                 xtype : 'TextItem',
29598                 text : "Width: ",
29599                 xns : rooui.Toolbar  //Boostrap?
29600             },
29601             {
29602                 xtype : 'ComboBox',
29603                 allowBlank : false,
29604                 displayField : 'val',
29605                 editable : true,
29606                 listWidth : 100,
29607                 triggerAction : 'all',
29608                 typeAhead : true,
29609                 valueField : 'val',
29610                 width : 100,
29611                 name : 'width',
29612                 listeners : {
29613                     select : function (combo, r, index)
29614                     {
29615                         toolbar.editorcore.selectNode(toolbar.tb.selectedNode);
29616                         var b = block();
29617                         b.width = r.get('val');
29618                         b.updateElement();
29619                         syncValue();
29620                         toolbar.editorcore.onEditorEvent();
29621                     }
29622                 },
29623                 xns : rooui.form,
29624                 store : {
29625                     xtype : 'SimpleStore',
29626                     data : [
29627                         ['100%'],
29628                         ['auto']
29629                     ],
29630                     fields : [ 'val'],
29631                     xns : Roo.data
29632                 }
29633             },
29634             // -------- Cols
29635             
29636             {
29637                 xtype : 'TextItem',
29638                 text : "Columns: ",
29639                 xns : rooui.Toolbar  //Boostrap?
29640             },
29641          
29642             {
29643                 xtype : 'Button',
29644                 text: '-',
29645                 listeners : {
29646                     click : function (_self, e)
29647                     {
29648                         toolbar.editorcore.selectNode(toolbar.tb.selectedNode);
29649                         block().removeColumn();
29650                         syncValue();
29651                         toolbar.editorcore.onEditorEvent();
29652                     }
29653                 },
29654                 xns : rooui.Toolbar
29655             },
29656             {
29657                 xtype : 'Button',
29658                 text: '+',
29659                 listeners : {
29660                     click : function (_self, e)
29661                     {
29662                         toolbar.editorcore.selectNode(toolbar.tb.selectedNode);
29663                         block().addColumn();
29664                         syncValue();
29665                         toolbar.editorcore.onEditorEvent();
29666                     }
29667                 },
29668                 xns : rooui.Toolbar
29669             },
29670             // -------- ROWS
29671             {
29672                 xtype : 'TextItem',
29673                 text : "Rows: ",
29674                 xns : rooui.Toolbar  //Boostrap?
29675             },
29676          
29677             {
29678                 xtype : 'Button',
29679                 text: '-',
29680                 listeners : {
29681                     click : function (_self, e)
29682                     {
29683                         toolbar.editorcore.selectNode(toolbar.tb.selectedNode);
29684                         block().removeRow();
29685                         syncValue();
29686                         toolbar.editorcore.onEditorEvent();
29687                     }
29688                 },
29689                 xns : rooui.Toolbar
29690             },
29691             {
29692                 xtype : 'Button',
29693                 text: '+',
29694                 listeners : {
29695                     click : function (_self, e)
29696                     {
29697                         block().addRow();
29698                         syncValue();
29699                         toolbar.editorcore.onEditorEvent();
29700                     }
29701                 },
29702                 xns : rooui.Toolbar
29703             },
29704             // -------- ROWS
29705             {
29706                 xtype : 'Button',
29707                 text: 'Reset Column Widths',
29708                 listeners : {
29709                     
29710                     click : function (_self, e)
29711                     {
29712                         block().resetWidths();
29713                         syncValue();
29714                         toolbar.editorcore.onEditorEvent();
29715                     }
29716                 },
29717                 xns : rooui.Toolbar
29718             } 
29719             
29720             
29721             
29722         ];
29723         
29724     },
29725     
29726     
29727   /**
29728      * create a DomHelper friendly object - for use with
29729      * Roo.DomHelper.markup / overwrite / etc..
29730      * ?? should it be called with option to hide all editing features?
29731      */
29732     toObject : function()
29733     {
29734         
29735         var ret = {
29736             tag : 'table',
29737             contenteditable : 'false', // this stops cell selection from picking the table.
29738             'data-block' : 'Table',
29739             style : {
29740                 width:  this.width,
29741                 border : 'solid 1px #000', // ??? hard coded?
29742                 'border-collapse' : 'collapse' 
29743             },
29744             cn : [
29745                 { tag : 'tbody' , cn : [] }
29746             ]
29747         };
29748         
29749         // do we have a head = not really 
29750         var ncols = 0;
29751         Roo.each(this.rows, function( row ) {
29752             var tr = {
29753                 tag: 'tr',
29754                 style : {
29755                     margin: '6px',
29756                     border : 'solid 1px #000',
29757                     textAlign : 'left' 
29758                 },
29759                 cn : [ ]
29760             };
29761             
29762             ret.cn[0].cn.push(tr);
29763             // does the row have any properties? ?? height?
29764             var nc = 0;
29765             Roo.each(row, function( cell ) {
29766                 
29767                 var td = {
29768                     tag : 'td',
29769                     contenteditable :  'true',
29770                     'data-block' : 'Td',
29771                     html : cell.html,
29772                     style : cell.style
29773                 };
29774                 if (cell.colspan > 1) {
29775                     td.colspan = cell.colspan ;
29776                     nc += cell.colspan;
29777                 } else {
29778                     nc++;
29779                 }
29780                 if (cell.rowspan > 1) {
29781                     td.rowspan = cell.rowspan ;
29782                 }
29783                 
29784                 
29785                 // widths ?
29786                 tr.cn.push(td);
29787                     
29788                 
29789             }, this);
29790             ncols = Math.max(nc, ncols);
29791             
29792             
29793         }, this);
29794         // add the header row..
29795         
29796         ncols++;
29797          
29798         
29799         return ret;
29800          
29801     },
29802     
29803     readElement : function(node)
29804     {
29805         node  = node ? node : this.node ;
29806         this.width = this.getVal(node, true, 'style', 'width') || '100%';
29807         
29808         this.rows = [];
29809         this.no_row = 0;
29810         var trs = Array.from(node.rows);
29811         trs.forEach(function(tr) {
29812             var row =  [];
29813             this.rows.push(row);
29814             
29815             this.no_row++;
29816             var no_column = 0;
29817             Array.from(tr.cells).forEach(function(td) {
29818                 
29819                 var add = {
29820                     colspan : td.hasAttribute('colspan') ? td.getAttribute('colspan')*1 : 1,
29821                     rowspan : td.hasAttribute('rowspan') ? td.getAttribute('rowspan')*1 : 1,
29822                     style : td.hasAttribute('style') ? td.getAttribute('style') : '',
29823                     html : td.innerHTML
29824                 };
29825                 no_column += add.colspan;
29826                      
29827                 
29828                 row.push(add);
29829                 
29830                 
29831             },this);
29832             this.no_col = Math.max(this.no_col, no_column);
29833             
29834             
29835         },this);
29836         
29837         
29838     },
29839     normalizeRows: function()
29840     {
29841         var ret= [];
29842         var rid = -1;
29843         this.rows.forEach(function(row) {
29844             rid++;
29845             ret[rid] = [];
29846             row = this.normalizeRow(row);
29847             var cid = 0;
29848             row.forEach(function(c) {
29849                 while (typeof(ret[rid][cid]) != 'undefined') {
29850                     cid++;
29851                 }
29852                 if (typeof(ret[rid]) == 'undefined') {
29853                     ret[rid] = [];
29854                 }
29855                 ret[rid][cid] = c;
29856                 c.row = rid;
29857                 c.col = cid;
29858                 if (c.rowspan < 2) {
29859                     return;
29860                 }
29861                 
29862                 for(var i = 1 ;i < c.rowspan; i++) {
29863                     if (typeof(ret[rid+i]) == 'undefined') {
29864                         ret[rid+i] = [];
29865                     }
29866                     ret[rid+i][cid] = c;
29867                 }
29868             });
29869         }, this);
29870         return ret;
29871     
29872     },
29873     
29874     normalizeRow: function(row)
29875     {
29876         var ret= [];
29877         row.forEach(function(c) {
29878             if (c.colspan < 2) {
29879                 ret.push(c);
29880                 return;
29881             }
29882             for(var i =0 ;i < c.colspan; i++) {
29883                 ret.push(c);
29884             }
29885         });
29886         return ret;
29887     
29888     },
29889     
29890     deleteColumn : function(sel)
29891     {
29892         if (!sel || sel.type != 'col') {
29893             return;
29894         }
29895         if (this.no_col < 2) {
29896             return;
29897         }
29898         
29899         this.rows.forEach(function(row) {
29900             var cols = this.normalizeRow(row);
29901             var col = cols[sel.col];
29902             if (col.colspan > 1) {
29903                 col.colspan --;
29904             } else {
29905                 row.remove(col);
29906             }
29907             
29908         }, this);
29909         this.no_col--;
29910         
29911     },
29912     removeColumn : function()
29913     {
29914         this.deleteColumn({
29915             type: 'col',
29916             col : this.no_col-1
29917         });
29918         this.updateElement();
29919     },
29920     
29921      
29922     addColumn : function()
29923     {
29924         
29925         this.rows.forEach(function(row) {
29926             row.push(this.emptyCell());
29927            
29928         }, this);
29929         this.updateElement();
29930     },
29931     
29932     deleteRow : function(sel)
29933     {
29934         if (!sel || sel.type != 'row') {
29935             return;
29936         }
29937         
29938         if (this.no_row < 2) {
29939             return;
29940         }
29941         
29942         var rows = this.normalizeRows();
29943         
29944         
29945         rows[sel.row].forEach(function(col) {
29946             if (col.rowspan > 1) {
29947                 col.rowspan--;
29948             } else {
29949                 col.remove = 1; // flage it as removed.
29950             }
29951             
29952         }, this);
29953         var newrows = [];
29954         this.rows.forEach(function(row) {
29955             newrow = [];
29956             row.forEach(function(c) {
29957                 if (typeof(c.remove) == 'undefined') {
29958                     newrow.push(c);
29959                 }
29960                 
29961             });
29962             if (newrow.length > 0) {
29963                 newrows.push(row);
29964             }
29965         });
29966         this.rows =  newrows;
29967         
29968         
29969         
29970         this.no_row--;
29971         this.updateElement();
29972         
29973     },
29974     removeRow : function()
29975     {
29976         this.deleteRow({
29977             type: 'row',
29978             row : this.no_row-1
29979         });
29980         
29981     },
29982     
29983      
29984     addRow : function()
29985     {
29986         
29987         var row = [];
29988         for (var i = 0; i < this.no_col; i++ ) {
29989             
29990             row.push(this.emptyCell());
29991            
29992         }
29993         this.rows.push(row);
29994         this.updateElement();
29995         
29996     },
29997      
29998     // the default cell object... at present...
29999     emptyCell : function() {
30000         return (new Roo.htmleditor.BlockTd({})).toObject();
30001         
30002      
30003     },
30004     
30005     removeNode : function()
30006     {
30007         return this.node;
30008     },
30009     
30010     
30011     
30012     resetWidths : function()
30013     {
30014         Array.from(this.node.getElementsByTagName('td')).forEach(function(n) {
30015             var nn = Roo.htmleditor.Block.factory(n);
30016             nn.width = '';
30017             nn.updateElement(n);
30018         });
30019     }
30020     
30021     
30022     
30023     
30024 })
30025
30026 /**
30027  *
30028  * editing a TD?
30029  *
30030  * since selections really work on the table cell, then editing really should work from there
30031  *
30032  * The original plan was to support merging etc... - but that may not be needed yet..
30033  *
30034  * So this simple version will support:
30035  *   add/remove cols
30036  *   adjust the width +/-
30037  *   reset the width...
30038  *   
30039  *
30040  */
30041
30042
30043  
30044
30045 /**
30046  * @class Roo.htmleditor.BlockTable
30047  * Block that manages a table
30048  * 
30049  * @constructor
30050  * Create a new Filter.
30051  * @param {Object} config Configuration options
30052  */
30053
30054 Roo.htmleditor.BlockTd = function(cfg)
30055 {
30056     if (cfg.node) {
30057         this.readElement(cfg.node);
30058         this.updateElement(cfg.node);
30059     }
30060     Roo.apply(this, cfg);
30061      
30062     
30063     
30064 }
30065 Roo.extend(Roo.htmleditor.BlockTd, Roo.htmleditor.Block, {
30066  
30067     node : false,
30068     
30069     width: '',
30070     textAlign : 'left',
30071     valign : 'top',
30072     
30073     colspan : 1,
30074     rowspan : 1,
30075     
30076     
30077     // used by context menu
30078     friendly_name : 'Table Cell',
30079     deleteTitle : false, // use our customer delete
30080     
30081     // context menu is drawn once..
30082     
30083     contextMenu : function(toolbar)
30084     {
30085         
30086         var cell = function() {
30087             return Roo.htmleditor.Block.factory(toolbar.tb.selectedNode);
30088         };
30089         
30090         var table = function() {
30091             return Roo.htmleditor.Block.factory(toolbar.tb.selectedNode.closest('table'));
30092         };
30093         
30094         var lr = false;
30095         var saveSel = function()
30096         {
30097             lr = toolbar.editorcore.getSelection().getRangeAt(0);
30098         }
30099         var restoreSel = function()
30100         {
30101             if (lr) {
30102                 (function() {
30103                     toolbar.editorcore.focus();
30104                     var cr = toolbar.editorcore.getSelection();
30105                     cr.removeAllRanges();
30106                     cr.addRange(lr);
30107                     toolbar.editorcore.onEditorEvent();
30108                 }).defer(10, this);
30109                 
30110                 
30111             }
30112         }
30113         
30114         var rooui =  typeof(Roo.bootstrap) == 'undefined' ? Roo : Roo.bootstrap;
30115         
30116         var syncValue = toolbar.editorcore.syncValue;
30117         
30118         var fields = {};
30119         
30120         return [
30121             {
30122                 xtype : 'Button',
30123                 text : 'Edit Table',
30124                 listeners : {
30125                     click : function() {
30126                         var t = toolbar.tb.selectedNode.closest('table');
30127                         toolbar.editorcore.selectNode(t);
30128                         toolbar.editorcore.onEditorEvent();                        
30129                     }
30130                 }
30131                 
30132             },
30133               
30134            
30135              
30136             {
30137                 xtype : 'TextItem',
30138                 text : "Column Width: ",
30139                  xns : rooui.Toolbar 
30140                
30141             },
30142             {
30143                 xtype : 'Button',
30144                 text: '-',
30145                 listeners : {
30146                     click : function (_self, e)
30147                     {
30148                         toolbar.editorcore.selectNode(toolbar.tb.selectedNode);
30149                         cell().shrinkColumn();
30150                         syncValue();
30151                          toolbar.editorcore.onEditorEvent();
30152                     }
30153                 },
30154                 xns : rooui.Toolbar
30155             },
30156             {
30157                 xtype : 'Button',
30158                 text: '+',
30159                 listeners : {
30160                     click : function (_self, e)
30161                     {
30162                         toolbar.editorcore.selectNode(toolbar.tb.selectedNode);
30163                         cell().growColumn();
30164                         syncValue();
30165                         toolbar.editorcore.onEditorEvent();
30166                     }
30167                 },
30168                 xns : rooui.Toolbar
30169             },
30170             
30171             {
30172                 xtype : 'TextItem',
30173                 text : "Vertical Align: ",
30174                 xns : rooui.Toolbar  //Boostrap?
30175             },
30176             {
30177                 xtype : 'ComboBox',
30178                 allowBlank : false,
30179                 displayField : 'val',
30180                 editable : true,
30181                 listWidth : 100,
30182                 triggerAction : 'all',
30183                 typeAhead : true,
30184                 valueField : 'val',
30185                 width : 100,
30186                 name : 'valign',
30187                 listeners : {
30188                     select : function (combo, r, index)
30189                     {
30190                         toolbar.editorcore.selectNode(toolbar.tb.selectedNode);
30191                         var b = cell();
30192                         b.valign = r.get('val');
30193                         b.updateElement();
30194                         syncValue();
30195                         toolbar.editorcore.onEditorEvent();
30196                     }
30197                 },
30198                 xns : rooui.form,
30199                 store : {
30200                     xtype : 'SimpleStore',
30201                     data : [
30202                         ['top'],
30203                         ['middle'],
30204                         ['bottom'] // there are afew more... 
30205                     ],
30206                     fields : [ 'val'],
30207                     xns : Roo.data
30208                 }
30209             },
30210             
30211             {
30212                 xtype : 'TextItem',
30213                 text : "Merge Cells: ",
30214                  xns : rooui.Toolbar 
30215                
30216             },
30217             
30218             
30219             {
30220                 xtype : 'Button',
30221                 text: 'Right',
30222                 listeners : {
30223                     click : function (_self, e)
30224                     {
30225                         toolbar.editorcore.selectNode(toolbar.tb.selectedNode);
30226                         cell().mergeRight();
30227                         //block().growColumn();
30228                         syncValue();
30229                         toolbar.editorcore.onEditorEvent();
30230                     }
30231                 },
30232                 xns : rooui.Toolbar
30233             },
30234              
30235             {
30236                 xtype : 'Button',
30237                 text: 'Below',
30238                 listeners : {
30239                     click : function (_self, e)
30240                     {
30241                         toolbar.editorcore.selectNode(toolbar.tb.selectedNode);
30242                         cell().mergeBelow();
30243                         //block().growColumn();
30244                         syncValue();
30245                         toolbar.editorcore.onEditorEvent();
30246                     }
30247                 },
30248                 xns : rooui.Toolbar
30249             },
30250             {
30251                 xtype : 'TextItem',
30252                 text : "| ",
30253                  xns : rooui.Toolbar 
30254                
30255             },
30256             
30257             {
30258                 xtype : 'Button',
30259                 text: 'Split',
30260                 listeners : {
30261                     click : function (_self, e)
30262                     {
30263                         //toolbar.editorcore.selectNode(toolbar.tb.selectedNode);
30264                         cell().split();
30265                         syncValue();
30266                         toolbar.editorcore.selectNode(toolbar.tb.selectedNode);
30267                         toolbar.editorcore.onEditorEvent();
30268                                              
30269                     }
30270                 },
30271                 xns : rooui.Toolbar
30272             },
30273             {
30274                 xtype : 'Fill',
30275                 xns : rooui.Toolbar 
30276                
30277             },
30278         
30279           
30280             {
30281                 xtype : 'Button',
30282                 text: 'Delete',
30283                  
30284                 xns : rooui.Toolbar,
30285                 menu : {
30286                     xtype : 'Menu',
30287                     xns : rooui.menu,
30288                     items : [
30289                         {
30290                             xtype : 'Item',
30291                             html: 'Column',
30292                             listeners : {
30293                                 click : function (_self, e)
30294                                 {
30295                                     var t = table();
30296                                     
30297                                     cell().deleteColumn();
30298                                     syncValue();
30299                                     toolbar.editorcore.selectNode(t.node);
30300                                     toolbar.editorcore.onEditorEvent();   
30301                                 }
30302                             },
30303                             xns : rooui.menu
30304                         },
30305                         {
30306                             xtype : 'Item',
30307                             html: 'Row',
30308                             listeners : {
30309                                 click : function (_self, e)
30310                                 {
30311                                     var t = table();
30312                                     cell().deleteRow();
30313                                     syncValue();
30314                                     
30315                                     toolbar.editorcore.selectNode(t.node);
30316                                     toolbar.editorcore.onEditorEvent();   
30317                                                          
30318                                 }
30319                             },
30320                             xns : rooui.menu
30321                         },
30322                        {
30323                             xtype : 'Separator',
30324                             xns : rooui.menu
30325                         },
30326                         {
30327                             xtype : 'Item',
30328                             html: 'Table',
30329                             listeners : {
30330                                 click : function (_self, e)
30331                                 {
30332                                     var t = table();
30333                                     var nn = t.node.nextSibling || t.node.previousSibling;
30334                                     t.node.parentNode.removeChild(t.node);
30335                                     if (nn) { 
30336                                         toolbar.editorcore.selectNode(nn, true);
30337                                     }
30338                                     toolbar.editorcore.onEditorEvent();   
30339                                                          
30340                                 }
30341                             },
30342                             xns : rooui.menu
30343                         }
30344                     ]
30345                 }
30346             }
30347             
30348             // align... << fixme
30349             
30350         ];
30351         
30352     },
30353     
30354     
30355   /**
30356      * create a DomHelper friendly object - for use with
30357      * Roo.DomHelper.markup / overwrite / etc..
30358      * ?? should it be called with option to hide all editing features?
30359      */
30360  /**
30361      * create a DomHelper friendly object - for use with
30362      * Roo.DomHelper.markup / overwrite / etc..
30363      * ?? should it be called with option to hide all editing features?
30364      */
30365     toObject : function()
30366     {
30367         var ret = {
30368             tag : 'td',
30369             contenteditable : 'true', // this stops cell selection from picking the table.
30370             'data-block' : 'Td',
30371             valign : this.valign,
30372             style : {  
30373                 'text-align' :  this.textAlign,
30374                 border : 'solid 1px rgb(0, 0, 0)', // ??? hard coded?
30375                 'border-collapse' : 'collapse',
30376                 padding : '6px', // 8 for desktop / 4 for mobile
30377                 'vertical-align': this.valign
30378             },
30379             html : this.html
30380         };
30381         if (this.width != '') {
30382             ret.width = this.width;
30383             ret.style.width = this.width;
30384         }
30385         
30386         
30387         if (this.colspan > 1) {
30388             ret.colspan = this.colspan ;
30389         } 
30390         if (this.rowspan > 1) {
30391             ret.rowspan = this.rowspan ;
30392         }
30393         
30394            
30395         
30396         return ret;
30397          
30398     },
30399     
30400     readElement : function(node)
30401     {
30402         node  = node ? node : this.node ;
30403         this.width = node.style.width;
30404         this.colspan = Math.max(1,1*node.getAttribute('colspan'));
30405         this.rowspan = Math.max(1,1*node.getAttribute('rowspan'));
30406         this.html = node.innerHTML;
30407         if (node.style.textAlign != '') {
30408             this.textAlign = node.style.textAlign;
30409         }
30410         
30411         
30412     },
30413      
30414     // the default cell object... at present...
30415     emptyCell : function() {
30416         return {
30417             colspan :  1,
30418             rowspan :  1,
30419             textAlign : 'left',
30420             html : "&nbsp;" // is this going to be editable now?
30421         };
30422      
30423     },
30424     
30425     removeNode : function()
30426     {
30427         return this.node.closest('table');
30428          
30429     },
30430     
30431     cellData : false,
30432     
30433     colWidths : false,
30434     
30435     toTableArray  : function()
30436     {
30437         var ret = [];
30438         var tab = this.node.closest('tr').closest('table');
30439         Array.from(tab.rows).forEach(function(r, ri){
30440             ret[ri] = [];
30441         });
30442         var rn = 0;
30443         this.colWidths = [];
30444         var all_auto = true;
30445         Array.from(tab.rows).forEach(function(r, ri){
30446             
30447             var cn = 0;
30448             Array.from(r.cells).forEach(function(ce, ci){
30449                 var c =  {
30450                     cell : ce,
30451                     row : rn,
30452                     col: cn,
30453                     colspan : ce.colSpan,
30454                     rowspan : ce.rowSpan
30455                 };
30456                 if (ce.isEqualNode(this.node)) {
30457                     this.cellData = c;
30458                 }
30459                 // if we have been filled up by a row?
30460                 if (typeof(ret[rn][cn]) != 'undefined') {
30461                     while(typeof(ret[rn][cn]) != 'undefined') {
30462                         cn++;
30463                     }
30464                     c.col = cn;
30465                 }
30466                 
30467                 if (typeof(this.colWidths[cn]) == 'undefined' && c.colspan < 2) {
30468                     this.colWidths[cn] =   ce.style.width;
30469                     if (this.colWidths[cn] != '') {
30470                         all_auto = false;
30471                     }
30472                 }
30473                 
30474                 
30475                 if (c.colspan < 2 && c.rowspan < 2 ) {
30476                     ret[rn][cn] = c;
30477                     cn++;
30478                     return;
30479                 }
30480                 for(var j = 0; j < c.rowspan; j++) {
30481                     if (typeof(ret[rn+j]) == 'undefined') {
30482                         continue; // we have a problem..
30483                     }
30484                     ret[rn+j][cn] = c;
30485                     for(var i = 0; i < c.colspan; i++) {
30486                         ret[rn+j][cn+i] = c;
30487                     }
30488                 }
30489                 
30490                 cn += c.colspan;
30491             }, this);
30492             rn++;
30493         }, this);
30494         
30495         // initalize widths.?
30496         // either all widths or no widths..
30497         if (all_auto) {
30498             this.colWidths[0] = false; // no widths flag.
30499         }
30500         
30501         
30502         return ret;
30503         
30504     },
30505     
30506     
30507     
30508     
30509     mergeRight: function()
30510     {
30511          
30512         // get the contents of the next cell along..
30513         var tr = this.node.closest('tr');
30514         var i = Array.prototype.indexOf.call(tr.childNodes, this.node);
30515         if (i >= tr.childNodes.length - 1) {
30516             return; // no cells on right to merge with.
30517         }
30518         var table = this.toTableArray();
30519         
30520         if (typeof(table[this.cellData.row][this.cellData.col+this.cellData.colspan]) == 'undefined') {
30521             return; // nothing right?
30522         }
30523         var rc = table[this.cellData.row][this.cellData.col+this.cellData.colspan];
30524         // right cell - must be same rowspan and on the same row.
30525         if (rc.rowspan != this.cellData.rowspan || rc.row != this.cellData.row) {
30526             return; // right hand side is not same rowspan.
30527         }
30528         
30529         
30530         
30531         this.node.innerHTML += ' ' + rc.cell.innerHTML;
30532         tr.removeChild(rc.cell);
30533         this.colspan += rc.colspan;
30534         this.node.setAttribute('colspan', this.colspan);
30535
30536         var table = this.toTableArray();
30537         this.normalizeWidths(table);
30538         this.updateWidths(table);
30539     },
30540     
30541     
30542     mergeBelow : function()
30543     {
30544         var table = this.toTableArray();
30545         if (typeof(table[this.cellData.row+this.cellData.rowspan]) == 'undefined') {
30546             return; // no row below
30547         }
30548         if (typeof(table[this.cellData.row+this.cellData.rowspan][this.cellData.col]) == 'undefined') {
30549             return; // nothing right?
30550         }
30551         var rc = table[this.cellData.row+this.cellData.rowspan][this.cellData.col];
30552         
30553         if (rc.colspan != this.cellData.colspan || rc.col != this.cellData.col) {
30554             return; // right hand side is not same rowspan.
30555         }
30556         this.node.innerHTML =  this.node.innerHTML + rc.cell.innerHTML ;
30557         rc.cell.parentNode.removeChild(rc.cell);
30558         this.rowspan += rc.rowspan;
30559         this.node.setAttribute('rowspan', this.rowspan);
30560     },
30561     
30562     split: function()
30563     {
30564         if (this.node.rowSpan < 2 && this.node.colSpan < 2) {
30565             return;
30566         }
30567         var table = this.toTableArray();
30568         var cd = this.cellData;
30569         this.rowspan = 1;
30570         this.colspan = 1;
30571         
30572         for(var r = cd.row; r < cd.row + cd.rowspan; r++) {
30573              
30574             
30575             for(var c = cd.col; c < cd.col + cd.colspan; c++) {
30576                 if (r == cd.row && c == cd.col) {
30577                     this.node.removeAttribute('rowspan');
30578                     this.node.removeAttribute('colspan');
30579                 }
30580                  
30581                 var ntd = this.node.cloneNode(); // which col/row should be 0..
30582                 ntd.removeAttribute('id'); 
30583                 ntd.style.width  = this.colWidths[c];
30584                 ntd.innerHTML = '';
30585                 table[r][c] = { cell : ntd, col : c, row: r , colspan : 1 , rowspan : 1   };
30586             }
30587             
30588         }
30589         this.redrawAllCells(table);
30590         
30591     },
30592     
30593     
30594     
30595     redrawAllCells: function(table)
30596     {
30597         
30598          
30599         var tab = this.node.closest('tr').closest('table');
30600         var ctr = tab.rows[0].parentNode;
30601         Array.from(tab.rows).forEach(function(r, ri){
30602             
30603             Array.from(r.cells).forEach(function(ce, ci){
30604                 ce.parentNode.removeChild(ce);
30605             });
30606             r.parentNode.removeChild(r);
30607         });
30608         for(var r = 0 ; r < table.length; r++) {
30609             var re = tab.rows[r];
30610             
30611             var re = tab.ownerDocument.createElement('tr');
30612             ctr.appendChild(re);
30613             for(var c = 0 ; c < table[r].length; c++) {
30614                 if (table[r][c].cell === false) {
30615                     continue;
30616                 }
30617                 
30618                 re.appendChild(table[r][c].cell);
30619                  
30620                 table[r][c].cell = false;
30621             }
30622         }
30623         
30624     },
30625     updateWidths : function(table)
30626     {
30627         for(var r = 0 ; r < table.length; r++) {
30628            
30629             for(var c = 0 ; c < table[r].length; c++) {
30630                 if (table[r][c].cell === false) {
30631                     continue;
30632                 }
30633                 
30634                 if (this.colWidths[0] != false && table[r][c].colspan < 2) {
30635                     var el = Roo.htmleditor.Block.factory(table[r][c].cell);
30636                     el.width = Math.floor(this.colWidths[c])  +'%';
30637                     el.updateElement(el.node);
30638                 }
30639                 if (this.colWidths[0] != false && table[r][c].colspan > 1) {
30640                     var el = Roo.htmleditor.Block.factory(table[r][c].cell);
30641                     var width = 0;
30642                     for(var i = 0; i < table[r][c].colspan; i ++) {
30643                         width += Math.floor(this.colWidths[c + i]);
30644                     }
30645                     el.width = width  +'%';
30646                     el.updateElement(el.node);
30647                 }
30648                 table[r][c].cell = false; // done
30649             }
30650         }
30651     },
30652     normalizeWidths : function(table)
30653     {
30654         if (this.colWidths[0] === false) {
30655             var nw = 100.0 / this.colWidths.length;
30656             this.colWidths.forEach(function(w,i) {
30657                 this.colWidths[i] = nw;
30658             },this);
30659             return;
30660         }
30661     
30662         var t = 0, missing = [];
30663         
30664         this.colWidths.forEach(function(w,i) {
30665             //if you mix % and
30666             this.colWidths[i] = this.colWidths[i] == '' ? 0 : (this.colWidths[i]+'').replace(/[^0-9]+/g,'')*1;
30667             var add =  this.colWidths[i];
30668             if (add > 0) {
30669                 t+=add;
30670                 return;
30671             }
30672             missing.push(i);
30673             
30674             
30675         },this);
30676         var nc = this.colWidths.length;
30677         if (missing.length) {
30678             var mult = (nc - missing.length) / (1.0 * nc);
30679             var t = mult * t;
30680             var ew = (100 -t) / (1.0 * missing.length);
30681             this.colWidths.forEach(function(w,i) {
30682                 if (w > 0) {
30683                     this.colWidths[i] = w * mult;
30684                     return;
30685                 }
30686                 
30687                 this.colWidths[i] = ew;
30688             }, this);
30689             // have to make up numbers..
30690              
30691         }
30692         // now we should have all the widths..
30693         
30694     
30695     },
30696     
30697     shrinkColumn : function()
30698     {
30699         var table = this.toTableArray();
30700         this.normalizeWidths(table);
30701         var col = this.cellData.col;
30702         var nw = this.colWidths[col] * 0.8;
30703         if (nw < 5) {
30704             return;
30705         }
30706         var otherAdd = (this.colWidths[col]  * 0.2) / (this.colWidths.length -1);
30707         this.colWidths.forEach(function(w,i) {
30708             if (i == col) {
30709                  this.colWidths[i] = nw;
30710                 return;
30711             }
30712             this.colWidths[i] += otherAdd
30713         }, this);
30714         this.updateWidths(table);
30715          
30716     },
30717     growColumn : function()
30718     {
30719         var table = this.toTableArray();
30720         this.normalizeWidths(table);
30721         var col = this.cellData.col;
30722         var nw = this.colWidths[col] * 1.2;
30723         if (nw > 90) {
30724             return;
30725         }
30726         var otherSub = (this.colWidths[col]  * 0.2) / (this.colWidths.length -1);
30727         this.colWidths.forEach(function(w,i) {
30728             if (i == col) {
30729                 this.colWidths[i] = nw;
30730                 return;
30731             }
30732             this.colWidths[i] -= otherSub
30733         }, this);
30734         this.updateWidths(table);
30735          
30736     },
30737     deleteRow : function()
30738     {
30739         // delete this rows 'tr'
30740         // if any of the cells in this row have a rowspan > 1 && row!= this row..
30741         // then reduce the rowspan.
30742         var table = this.toTableArray();
30743         // this.cellData.row;
30744         for (var i =0;i< table[this.cellData.row].length ; i++) {
30745             var c = table[this.cellData.row][i];
30746             if (c.row != this.cellData.row) {
30747                 
30748                 c.rowspan--;
30749                 c.cell.setAttribute('rowspan', c.rowspan);
30750                 continue;
30751             }
30752             if (c.rowspan > 1) {
30753                 c.rowspan--;
30754                 c.cell.setAttribute('rowspan', c.rowspan);
30755             }
30756         }
30757         table.splice(this.cellData.row,1);
30758         this.redrawAllCells(table);
30759         
30760     },
30761     deleteColumn : function()
30762     {
30763         var table = this.toTableArray();
30764         
30765         for (var i =0;i< table.length ; i++) {
30766             var c = table[i][this.cellData.col];
30767             if (c.col != this.cellData.col) {
30768                 table[i][this.cellData.col].colspan--;
30769             } else if (c.colspan > 1) {
30770                 c.colspan--;
30771                 c.cell.setAttribute('colspan', c.colspan);
30772             }
30773             table[i].splice(this.cellData.col,1);
30774         }
30775         
30776         this.redrawAllCells(table);
30777     }
30778     
30779     
30780     
30781     
30782 })
30783
30784 //<script type="text/javascript">
30785
30786 /*
30787  * Based  Ext JS Library 1.1.1
30788  * Copyright(c) 2006-2007, Ext JS, LLC.
30789  * LGPL
30790  *
30791  */
30792  
30793 /**
30794  * @class Roo.HtmlEditorCore
30795  * @extends Roo.Component
30796  * Provides a the editing component for the HTML editors in Roo. (bootstrap and Roo.form)
30797  *
30798  * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
30799  */
30800
30801 Roo.HtmlEditorCore = function(config){
30802     
30803     
30804     Roo.HtmlEditorCore.superclass.constructor.call(this, config);
30805     
30806     
30807     this.addEvents({
30808         /**
30809          * @event initialize
30810          * Fires when the editor is fully initialized (including the iframe)
30811          * @param {Roo.HtmlEditorCore} this
30812          */
30813         initialize: true,
30814         /**
30815          * @event activate
30816          * Fires when the editor is first receives the focus. Any insertion must wait
30817          * until after this event.
30818          * @param {Roo.HtmlEditorCore} this
30819          */
30820         activate: true,
30821          /**
30822          * @event beforesync
30823          * Fires before the textarea is updated with content from the editor iframe. Return false
30824          * to cancel the sync.
30825          * @param {Roo.HtmlEditorCore} this
30826          * @param {String} html
30827          */
30828         beforesync: true,
30829          /**
30830          * @event beforepush
30831          * Fires before the iframe editor is updated with content from the textarea. Return false
30832          * to cancel the push.
30833          * @param {Roo.HtmlEditorCore} this
30834          * @param {String} html
30835          */
30836         beforepush: true,
30837          /**
30838          * @event sync
30839          * Fires when the textarea is updated with content from the editor iframe.
30840          * @param {Roo.HtmlEditorCore} this
30841          * @param {String} html
30842          */
30843         sync: true,
30844          /**
30845          * @event push
30846          * Fires when the iframe editor is updated with content from the textarea.
30847          * @param {Roo.HtmlEditorCore} this
30848          * @param {String} html
30849          */
30850         push: true,
30851         
30852         /**
30853          * @event editorevent
30854          * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
30855          * @param {Roo.HtmlEditorCore} this
30856          */
30857         editorevent: true 
30858         
30859         
30860     });
30861     
30862     // at this point this.owner is set, so we can start working out the whitelisted / blacklisted elements
30863     
30864     // defaults : white / black...
30865     this.applyBlacklists();
30866     
30867     
30868     
30869 };
30870
30871
30872 Roo.extend(Roo.HtmlEditorCore, Roo.Component,  {
30873
30874
30875      /**
30876      * @cfg {Roo.form.HtmlEditor|Roo.bootstrap.HtmlEditor} the owner field 
30877      */
30878     
30879     owner : false,
30880     
30881      /**
30882      * @cfg {String} css styling for resizing. (used on bootstrap only)
30883      */
30884     resize : false,
30885      /**
30886      * @cfg {Number} height (in pixels)
30887      */   
30888     height: 300,
30889    /**
30890      * @cfg {Number} width (in pixels)
30891      */   
30892     width: 500,
30893      /**
30894      * @cfg {boolean} autoClean - default true - loading and saving will remove quite a bit of formating,
30895      *         if you are doing an email editor, this probably needs disabling, it's designed
30896      */
30897     autoClean: true,
30898     
30899     /**
30900      * @cfg {boolean} enableBlocks - default true - if the block editor (table and figure should be enabled)
30901      */
30902     enableBlocks : true,
30903     /**
30904      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
30905      * 
30906      */
30907     stylesheets: false,
30908      /**
30909      * @cfg {String} language default en - language of text (usefull for rtl languages)
30910      * 
30911      */
30912     language: 'en',
30913     
30914     /**
30915      * @cfg {boolean} allowComments - default false - allow comments in HTML source
30916      *          - by default they are stripped - if you are editing email you may need this.
30917      */
30918     allowComments: false,
30919     // id of frame..
30920     frameId: false,
30921     
30922     // private properties
30923     validationEvent : false,
30924     deferHeight: true,
30925     initialized : false,
30926     activated : false,
30927     sourceEditMode : false,
30928     onFocus : Roo.emptyFn,
30929     iframePad:3,
30930     hideMode:'offsets',
30931     
30932     clearUp: true,
30933     
30934     // blacklist + whitelisted elements..
30935     black: false,
30936     white: false,
30937      
30938     bodyCls : '',
30939
30940     
30941     undoManager : false,
30942     /**
30943      * Protected method that will not generally be called directly. It
30944      * is called when the editor initializes the iframe with HTML contents. Override this method if you
30945      * want to change the initialization markup of the iframe (e.g. to add stylesheets).
30946      */
30947     getDocMarkup : function(){
30948         // body styles..
30949         var st = '';
30950         
30951         // inherit styels from page...?? 
30952         if (this.stylesheets === false) {
30953             
30954             Roo.get(document.head).select('style').each(function(node) {
30955                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
30956             });
30957             
30958             Roo.get(document.head).select('link').each(function(node) { 
30959                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
30960             });
30961             
30962         } else if (!this.stylesheets.length) {
30963                 // simple..
30964                 st = '<style type="text/css">' +
30965                     'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
30966                    '</style>';
30967         } else {
30968             for (var i in this.stylesheets) {
30969                 if (typeof(this.stylesheets[i]) != 'string') {
30970                     continue;
30971                 }
30972                 st += '<link rel="stylesheet" href="' + this.stylesheets[i] +'" type="text/css">';
30973             }
30974             
30975         }
30976         
30977         st +=  '<style type="text/css">' +
30978             'IMG { cursor: pointer } ' +
30979         '</style>';
30980         
30981         st += '<meta name="google" content="notranslate">';
30982         
30983         var cls = 'notranslate roo-htmleditor-body';
30984         
30985         if(this.bodyCls.length){
30986             cls += ' ' + this.bodyCls;
30987         }
30988         
30989         return '<html  class="notranslate" translate="no"><head>' + st  +
30990             //<style type="text/css">' +
30991             //'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
30992             //'</style>' +
30993             ' </head><body contenteditable="true" data-enable-grammerly="true" class="' +  cls + '"></body></html>';
30994     },
30995
30996     // private
30997     onRender : function(ct, position)
30998     {
30999         var _t = this;
31000         //Roo.HtmlEditorCore.superclass.onRender.call(this, ct, position);
31001         this.el = this.owner.inputEl ? this.owner.inputEl() : this.owner.el;
31002         
31003         
31004         this.el.dom.style.border = '0 none';
31005         this.el.dom.setAttribute('tabIndex', -1);
31006         this.el.addClass('x-hidden hide');
31007         
31008         
31009         
31010         if(Roo.isIE){ // fix IE 1px bogus margin
31011             this.el.applyStyles('margin-top:-1px;margin-bottom:-1px;')
31012         }
31013        
31014         
31015         this.frameId = Roo.id();
31016         
31017         var ifcfg = {
31018             tag: 'iframe',
31019             cls: 'form-control', // bootstrap..
31020             id: this.frameId,
31021             name: this.frameId,
31022             frameBorder : 'no',
31023             'src' : Roo.SSL_SECURE_URL ? Roo.SSL_SECURE_URL  :  "javascript:false"
31024         };
31025         if (this.resize) {
31026             ifcfg.style = { resize : this.resize };
31027         }
31028         
31029         var iframe = this.owner.wrap.createChild(ifcfg, this.el); 
31030         
31031         
31032         this.iframe = iframe.dom;
31033
31034         this.assignDocWin();
31035         
31036         this.doc.designMode = 'on';
31037        
31038         this.doc.open();
31039         this.doc.write(this.getDocMarkup());
31040         this.doc.close();
31041
31042         
31043         var task = { // must defer to wait for browser to be ready
31044             run : function(){
31045                 //console.log("run task?" + this.doc.readyState);
31046                 this.assignDocWin();
31047                 if(this.doc.body || this.doc.readyState == 'complete'){
31048                     try {
31049                         this.doc.designMode="on";
31050                         
31051                     } catch (e) {
31052                         return;
31053                     }
31054                     Roo.TaskMgr.stop(task);
31055                     this.initEditor.defer(10, this);
31056                 }
31057             },
31058             interval : 10,
31059             duration: 10000,
31060             scope: this
31061         };
31062         Roo.TaskMgr.start(task);
31063
31064     },
31065
31066     // private
31067     onResize : function(w, h)
31068     {
31069          Roo.log('resize: ' +w + ',' + h );
31070         //Roo.HtmlEditorCore.superclass.onResize.apply(this, arguments);
31071         if(!this.iframe){
31072             return;
31073         }
31074         if(typeof w == 'number'){
31075             
31076             this.iframe.style.width = w + 'px';
31077         }
31078         if(typeof h == 'number'){
31079             
31080             this.iframe.style.height = h + 'px';
31081             if(this.doc){
31082                 (this.doc.body || this.doc.documentElement).style.height = (h - (this.iframePad*2)) + 'px';
31083             }
31084         }
31085         
31086     },
31087
31088     /**
31089      * Toggles the editor between standard and source edit mode.
31090      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
31091      */
31092     toggleSourceEdit : function(sourceEditMode){
31093         
31094         this.sourceEditMode = sourceEditMode === true;
31095         
31096         if(this.sourceEditMode){
31097  
31098             Roo.get(this.iframe).addClass(['x-hidden','hide', 'd-none']);     //FIXME - what's the BS styles for these
31099             
31100         }else{
31101             Roo.get(this.iframe).removeClass(['x-hidden','hide', 'd-none']);
31102             //this.iframe.className = '';
31103             this.deferFocus();
31104         }
31105         //this.setSize(this.owner.wrap.getSize());
31106         //this.fireEvent('editmodechange', this, this.sourceEditMode);
31107     },
31108
31109     
31110   
31111
31112     /**
31113      * Protected method that will not generally be called directly. If you need/want
31114      * custom HTML cleanup, this is the method you should override.
31115      * @param {String} html The HTML to be cleaned
31116      * return {String} The cleaned HTML
31117      */
31118     cleanHtml : function(html)
31119     {
31120         html = String(html);
31121         if(html.length > 5){
31122             if(Roo.isSafari){ // strip safari nonsense
31123                 html = html.replace(/\sclass="(?:Apple-style-span|khtml-block-placeholder)"/gi, '');
31124             }
31125         }
31126         if(html == '&nbsp;'){
31127             html = '';
31128         }
31129         return html;
31130     },
31131
31132     /**
31133      * HTML Editor -> Textarea
31134      * Protected method that will not generally be called directly. Syncs the contents
31135      * of the editor iframe with the textarea.
31136      */
31137     syncValue : function()
31138     {
31139         //Roo.log("HtmlEditorCore:syncValue (EDITOR->TEXT)");
31140         if(this.initialized){
31141             
31142             if (this.undoManager) {
31143                 this.undoManager.addEvent();
31144             }
31145
31146             
31147             var bd = (this.doc.body || this.doc.documentElement);
31148            
31149             
31150             var sel = this.win.getSelection();
31151             
31152             var div = document.createElement('div');
31153             div.innerHTML = bd.innerHTML;
31154             var gtx = div.getElementsByClassName('gtx-trans-icon'); // google translate - really annoying and difficult to get rid of.
31155             if (gtx.length > 0) {
31156                 var rm = gtx.item(0).parentNode;
31157                 rm.parentNode.removeChild(rm);
31158             }
31159             
31160            
31161             if (this.enableBlocks) {
31162                 new Roo.htmleditor.FilterBlock({ node : div });
31163             }
31164             
31165             var html = div.innerHTML;
31166             
31167             //?? tidy?
31168             if (this.autoClean) {
31169                 
31170                 new Roo.htmleditor.FilterAttributes({
31171                     node : div,
31172                     attrib_white : [
31173                             'href',
31174                             'src',
31175                             'name',
31176                             'align',
31177                             'colspan',
31178                             'rowspan',
31179                             'data-display',
31180                             'data-width',
31181                             'data-caption',
31182                             'start' ,
31183                             'style',
31184                             // youtube embed.
31185                             'class',
31186                             'allowfullscreen',
31187                             'frameborder',
31188                             'width',
31189                             'height',
31190                             'alt'
31191                             ],
31192                     attrib_clean : ['href', 'src' ] 
31193                 });
31194                 
31195                 var tidy = new Roo.htmleditor.TidySerializer({
31196                     inner:  true
31197                 });
31198                 html  = tidy.serialize(div);
31199                 
31200             }
31201             
31202             
31203             if(Roo.isSafari){
31204                 var bs = bd.getAttribute('style'); // Safari puts text-align styles on the body element!
31205                 var m = bs ? bs.match(/text-align:(.*?);/i) : false;
31206                 if(m && m[1]){
31207                     html = '<div style="'+m[0]+'">' + html + '</div>';
31208                 }
31209             }
31210             html = this.cleanHtml(html);
31211             // fix up the special chars.. normaly like back quotes in word...
31212             // however we do not want to do this with chinese..
31213             html = html.replace(/[\uD800-\uDBFF][\uDC00-\uDFFF]|[\u0080-\uFFFF]/g, function(match) {
31214                 
31215                 var cc = match.charCodeAt();
31216
31217                 // Get the character value, handling surrogate pairs
31218                 if (match.length == 2) {
31219                     // It's a surrogate pair, calculate the Unicode code point
31220                     var high = match.charCodeAt(0) - 0xD800;
31221                     var low  = match.charCodeAt(1) - 0xDC00;
31222                     cc = (high * 0x400) + low + 0x10000;
31223                 }  else if (
31224                     (cc >= 0x4E00 && cc < 0xA000 ) ||
31225                     (cc >= 0x3400 && cc < 0x4E00 ) ||
31226                     (cc >= 0xf900 && cc < 0xfb00 )
31227                 ) {
31228                         return match;
31229                 }  
31230          
31231                 // No, use a numeric entity. Here we brazenly (and possibly mistakenly)
31232                 return "&#" + cc + ";";
31233                 
31234                 
31235             });
31236             
31237             
31238              
31239             if(this.owner.fireEvent('beforesync', this, html) !== false){
31240                 this.el.dom.value = html;
31241                 this.owner.fireEvent('sync', this, html);
31242             }
31243         }
31244     },
31245
31246     /**
31247      * TEXTAREA -> EDITABLE
31248      * Protected method that will not generally be called directly. Pushes the value of the textarea
31249      * into the iframe editor.
31250      */
31251     pushValue : function()
31252     {
31253         //Roo.log("HtmlEditorCore:pushValue (TEXT->EDITOR)");
31254         if(this.initialized){
31255             var v = this.el.dom.value.trim();
31256             
31257             
31258             if(this.owner.fireEvent('beforepush', this, v) !== false){
31259                 var d = (this.doc.body || this.doc.documentElement);
31260                 d.innerHTML = v;
31261                  
31262                 this.el.dom.value = d.innerHTML;
31263                 this.owner.fireEvent('push', this, v);
31264             }
31265             if (this.autoClean) {
31266                 new Roo.htmleditor.FilterParagraph({node : this.doc.body}); // paragraphs
31267                 new Roo.htmleditor.FilterSpan({node : this.doc.body}); // empty spans
31268             }
31269             if (this.enableBlocks) {
31270                 Roo.htmleditor.Block.initAll(this.doc.body);
31271             }
31272             
31273             this.updateLanguage();
31274             
31275             var lc = this.doc.body.lastChild;
31276             if (lc && lc.nodeType == 1 && lc.getAttribute("contenteditable") == "false") {
31277                 // add an extra line at the end.
31278                 this.doc.body.appendChild(this.doc.createElement('br'));
31279             }
31280             
31281             
31282         }
31283     },
31284
31285     // private
31286     deferFocus : function(){
31287         this.focus.defer(10, this);
31288     },
31289
31290     // doc'ed in Field
31291     focus : function(){
31292         if(this.win && !this.sourceEditMode){
31293             this.win.focus();
31294         }else{
31295             this.el.focus();
31296         }
31297     },
31298     
31299     assignDocWin: function()
31300     {
31301         var iframe = this.iframe;
31302         
31303          if(Roo.isIE){
31304             this.doc = iframe.contentWindow.document;
31305             this.win = iframe.contentWindow;
31306         } else {
31307 //            if (!Roo.get(this.frameId)) {
31308 //                return;
31309 //            }
31310 //            this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
31311 //            this.win = Roo.get(this.frameId).dom.contentWindow;
31312             
31313             if (!Roo.get(this.frameId) && !iframe.contentDocument) {
31314                 return;
31315             }
31316             
31317             this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
31318             this.win = (iframe.contentWindow || Roo.get(this.frameId).dom.contentWindow);
31319         }
31320     },
31321     
31322     // private
31323     initEditor : function(){
31324         //console.log("INIT EDITOR");
31325         this.assignDocWin();
31326         
31327         
31328         
31329         this.doc.designMode="on";
31330         this.doc.open();
31331         this.doc.write(this.getDocMarkup());
31332         this.doc.close();
31333         
31334         var dbody = (this.doc.body || this.doc.documentElement);
31335         //var ss = this.el.getStyles('font-size', 'font-family', 'background-image', 'background-repeat');
31336         // this copies styles from the containing element into thsi one..
31337         // not sure why we need all of this..
31338         //var ss = this.el.getStyles('font-size', 'background-image', 'background-repeat');
31339         
31340         //var ss = this.el.getStyles( 'background-image', 'background-repeat');
31341         //ss['background-attachment'] = 'fixed'; // w3c
31342         dbody.bgProperties = 'fixed'; // ie
31343         dbody.setAttribute("translate", "no");
31344         
31345         //Roo.DomHelper.applyStyles(dbody, ss);
31346         Roo.EventManager.on(this.doc, {
31347              
31348             'mouseup': this.onEditorEvent,
31349             'dblclick': this.onEditorEvent,
31350             'click': this.onEditorEvent,
31351             'keyup': this.onEditorEvent,
31352             
31353             buffer:100,
31354             scope: this
31355         });
31356         Roo.EventManager.on(this.doc, {
31357             'paste': this.onPasteEvent,
31358             scope : this
31359         });
31360         if(Roo.isGecko){
31361             Roo.EventManager.on(this.doc, 'keypress', this.mozKeyPress, this);
31362         }
31363         //??? needed???
31364         if(Roo.isIE || Roo.isSafari || Roo.isOpera){
31365             Roo.EventManager.on(this.doc, 'keydown', this.fixKeys, this);
31366         }
31367         this.initialized = true;
31368
31369         
31370         // initialize special key events - enter
31371         new Roo.htmleditor.KeyEnter({core : this});
31372         
31373          
31374         
31375         this.owner.fireEvent('initialize', this);
31376         this.pushValue();
31377     },
31378     // this is to prevent a href clicks resulting in a redirect?
31379    
31380     onPasteEvent : function(e,v)
31381     {
31382         // I think we better assume paste is going to be a dirty load of rubish from word..
31383         
31384         // even pasting into a 'email version' of this widget will have to clean up that mess.
31385         var cd = (e.browserEvent.clipboardData || window.clipboardData);
31386         
31387         // check what type of paste - if it's an image, then handle it differently.
31388         if (cd.files && cd.files.length > 0 && cd.types.indexOf('text/html') < 0) {
31389             // pasting images? 
31390             var urlAPI = (window.createObjectURL && window) || 
31391                 (window.URL && URL.revokeObjectURL && URL) || 
31392                 (window.webkitURL && webkitURL);
31393             
31394             var r = new FileReader();
31395             var t = this;
31396             r.addEventListener('load',function()
31397             {
31398                 
31399                 var d = (new DOMParser().parseFromString('<img src="' + r.result+ '">', 'text/html')).body;
31400                 // is insert asycn?
31401                 if (t.enableBlocks) {
31402                     
31403                     Array.from(d.getElementsByTagName('img')).forEach(function(img) {
31404                         if (img.closest('figure')) { // assume!! that it's aready
31405                             return;
31406                         }
31407                         var fig  = new Roo.htmleditor.BlockFigure({
31408                             image_src  : img.src
31409                         });
31410                         fig.updateElement(img); // replace it..
31411                         
31412                     });
31413                 }
31414                 t.insertAtCursor(d.innerHTML.replace(/&nbsp;/g,' '));
31415                 t.owner.fireEvent('paste', this);
31416             });
31417             r.readAsDataURL(cd.files[0]);
31418             
31419             e.preventDefault();
31420             
31421             return false;
31422         }
31423         if (cd.types.indexOf('text/html') < 0 ) {
31424             return false;
31425         }
31426         var images = [];
31427         var html = cd.getData('text/html'); // clipboard event
31428         if (cd.types.indexOf('text/rtf') > -1) {
31429             var parser = new Roo.rtf.Parser(cd.getData('text/rtf'));
31430             images = parser.doc ? parser.doc.getElementsByType('pict') : [];
31431         }
31432         //Roo.log(images);
31433         //Roo.log(imgs);
31434         // fixme..
31435         images = images.filter(function(g) { return !g.path.match(/^rtf\/(head|pgdsctbl|listtable|footerf)/); }) // ignore headers/footers etc.
31436                        .map(function(g) { return g.toDataURL(); })
31437                        .filter(function(g) { return g != 'about:blank'; });
31438         
31439         //Roo.log(html);
31440         html = this.cleanWordChars(html);
31441         
31442         var d = (new DOMParser().parseFromString(html, 'text/html')).body;
31443         
31444         
31445         var sn = this.getParentElement();
31446         // check if d contains a table, and prevent nesting??
31447         //Roo.log(d.getElementsByTagName('table'));
31448         //Roo.log(sn);
31449         //Roo.log(sn.closest('table'));
31450         if (d.getElementsByTagName('table').length && sn && sn.closest('table')) {
31451             e.preventDefault();
31452             this.insertAtCursor("You can not nest tables");
31453             //Roo.log("prevent?"); // fixme - 
31454             return false;
31455         }
31456         
31457         
31458         
31459         if (images.length > 0) {
31460             // replace all v:imagedata - with img.
31461             var ar = Array.from(d.getElementsByTagName('v:imagedata'));
31462             Roo.each(ar, function(node) {
31463                 node.parentNode.insertBefore(d.ownerDocument.createElement('img'), node );
31464                 node.parentNode.removeChild(node);
31465             });
31466             
31467             
31468             Roo.each(d.getElementsByTagName('img'), function(img, i) {
31469                 img.setAttribute('src', images[i]);
31470             });
31471         }
31472         if (this.autoClean) {
31473             new Roo.htmleditor.FilterWord({ node : d });
31474             
31475             new Roo.htmleditor.FilterStyleToTag({ node : d });
31476             new Roo.htmleditor.FilterAttributes({
31477                 node : d,
31478                 attrib_white : ['href', 'src', 'name', 'align', 'colspan', 'rowspan', 'data-display', 'data-width', 'start'],
31479                 attrib_clean : ['href', 'src' ] 
31480             });
31481             new Roo.htmleditor.FilterBlack({ node : d, tag : this.black});
31482             // should be fonts..
31483             new Roo.htmleditor.FilterKeepChildren({node : d, tag : [ 'FONT', ':' ]} );
31484             new Roo.htmleditor.FilterParagraph({ node : d });
31485             new Roo.htmleditor.FilterSpan({ node : d });
31486             new Roo.htmleditor.FilterLongBr({ node : d });
31487             new Roo.htmleditor.FilterComment({ node : d });
31488             
31489             
31490         }
31491         if (this.enableBlocks) {
31492                 
31493             Array.from(d.getElementsByTagName('img')).forEach(function(img) {
31494                 if (img.closest('figure')) { // assume!! that it's aready
31495                     return;
31496                 }
31497                 var fig  = new Roo.htmleditor.BlockFigure({
31498                     image_src  : img.src
31499                 });
31500                 fig.updateElement(img); // replace it..
31501                 
31502             });
31503         }
31504         
31505         
31506         this.insertAtCursor(d.innerHTML.replace(/&nbsp;/g,' '));
31507         if (this.enableBlocks) {
31508             Roo.htmleditor.Block.initAll(this.doc.body);
31509         }
31510          
31511         
31512         e.preventDefault();
31513         this.owner.fireEvent('paste', this);
31514         return false;
31515         // default behaveiour should be our local cleanup paste? (optional?)
31516         // for simple editor - we want to hammer the paste and get rid of everything... - so over-rideable..
31517         //this.owner.fireEvent('paste', e, v);
31518     },
31519     // private
31520     onDestroy : function(){
31521         
31522         
31523         
31524         if(this.rendered){
31525             
31526             //for (var i =0; i < this.toolbars.length;i++) {
31527             //    // fixme - ask toolbars for heights?
31528             //    this.toolbars[i].onDestroy();
31529            // }
31530             
31531             //this.wrap.dom.innerHTML = '';
31532             //this.wrap.remove();
31533         }
31534     },
31535
31536     // private
31537     onFirstFocus : function(){
31538         
31539         this.assignDocWin();
31540         this.undoManager = new Roo.lib.UndoManager(100,(this.doc.body || this.doc.documentElement));
31541         
31542         this.activated = true;
31543          
31544     
31545         if(Roo.isGecko){ // prevent silly gecko errors
31546             this.win.focus();
31547             var s = this.win.getSelection();
31548             if(!s.focusNode || s.focusNode.nodeType != 3){
31549                 var r = s.getRangeAt(0);
31550                 r.selectNodeContents((this.doc.body || this.doc.documentElement));
31551                 r.collapse(true);
31552                 this.deferFocus();
31553             }
31554             try{
31555                 this.execCmd('useCSS', true);
31556                 this.execCmd('styleWithCSS', false);
31557             }catch(e){}
31558         }
31559         this.owner.fireEvent('activate', this);
31560     },
31561
31562     // private
31563     adjustFont: function(btn){
31564         var adjust = btn.cmd == 'increasefontsize' ? 1 : -1;
31565         //if(Roo.isSafari){ // safari
31566         //    adjust *= 2;
31567        // }
31568         var v = parseInt(this.doc.queryCommandValue('FontSize')|| 3, 10);
31569         if(Roo.isSafari){ // safari
31570             var sm = { 10 : 1, 13: 2, 16:3, 18:4, 24: 5, 32:6, 48: 7 };
31571             v =  (v < 10) ? 10 : v;
31572             v =  (v > 48) ? 48 : v;
31573             v = typeof(sm[v]) == 'undefined' ? 1 : sm[v];
31574             
31575         }
31576         
31577         
31578         v = Math.max(1, v+adjust);
31579         
31580         this.execCmd('FontSize', v  );
31581     },
31582
31583     onEditorEvent : function(e)
31584     {
31585          
31586         
31587         if (e && (e.ctrlKey || e.metaKey) && e.keyCode === 90) {
31588             return; // we do not handle this.. (undo manager does..)
31589         }
31590         // clicking a 'block'?
31591         
31592         // in theory this detects if the last element is not a br, then we try and do that.
31593         // its so clicking in space at bottom triggers adding a br and moving the cursor.
31594         if (e &&
31595             e.target.nodeName == 'BODY' &&
31596             e.type == "mouseup" &&
31597             this.doc.body.lastChild
31598            ) {
31599             var lc = this.doc.body.lastChild;
31600             // gtx-trans is google translate plugin adding crap.
31601             while ((lc.nodeType == 3 && lc.nodeValue == '') || lc.id == 'gtx-trans') {
31602                 lc = lc.previousSibling;
31603             }
31604             if (lc.nodeType == 1 && lc.nodeName != 'BR') {
31605             // if last element is <BR> - then dont do anything.
31606             
31607                 var ns = this.doc.createElement('br');
31608                 this.doc.body.appendChild(ns);
31609                 range = this.doc.createRange();
31610                 range.setStartAfter(ns);
31611                 range.collapse(true);
31612                 var sel = this.win.getSelection();
31613                 sel.removeAllRanges();
31614                 sel.addRange(range);
31615             }
31616         }
31617         
31618         
31619         
31620         this.fireEditorEvent(e);
31621       //  this.updateToolbar();
31622         this.syncValue(); //we can not sync so often.. sync cleans, so this breaks stuff
31623     },
31624     
31625     fireEditorEvent: function(e)
31626     {
31627         this.owner.fireEvent('editorevent', this, e);
31628     },
31629
31630     insertTag : function(tg)
31631     {
31632         // could be a bit smarter... -> wrap the current selected tRoo..
31633         if (tg.toLowerCase() == 'span' ||
31634             tg.toLowerCase() == 'code' ||
31635             tg.toLowerCase() == 'sup' ||
31636             tg.toLowerCase() == 'sub' 
31637             ) {
31638             
31639             range = this.createRange(this.getSelection());
31640             var wrappingNode = this.doc.createElement(tg.toLowerCase());
31641             wrappingNode.appendChild(range.extractContents());
31642             range.insertNode(wrappingNode);
31643
31644             return;
31645             
31646             
31647             
31648         }
31649         this.execCmd("formatblock",   tg);
31650         this.undoManager.addEvent(); 
31651     },
31652     
31653     insertText : function(txt)
31654     {
31655         
31656         
31657         var range = this.createRange();
31658         range.deleteContents();
31659                //alert(Sender.getAttribute('label'));
31660                
31661         range.insertNode(this.doc.createTextNode(txt));
31662         this.undoManager.addEvent();
31663     } ,
31664     
31665      
31666
31667     /**
31668      * Executes a Midas editor command on the editor document and performs necessary focus and
31669      * toolbar updates. <b>This should only be called after the editor is initialized.</b>
31670      * @param {String} cmd The Midas command
31671      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
31672      */
31673     relayCmd : function(cmd, value)
31674     {
31675         
31676         switch (cmd) {
31677             case 'justifyleft':
31678             case 'justifyright':
31679             case 'justifycenter':
31680                 // if we are in a cell, then we will adjust the
31681                 var n = this.getParentElement();
31682                 var td = n.closest('td');
31683                 if (td) {
31684                     var bl = Roo.htmleditor.Block.factory(td);
31685                     bl.textAlign = cmd.replace('justify','');
31686                     bl.updateElement();
31687                     this.owner.fireEvent('editorevent', this);
31688                     return;
31689                 }
31690                 this.execCmd('styleWithCSS', true); // 
31691                 break;
31692             case 'bold':
31693             case 'italic':
31694             case 'underline':                
31695                 // if there is no selection, then we insert, and set the curson inside it..
31696                 this.execCmd('styleWithCSS', false); 
31697                 break;
31698                 
31699         
31700             default:
31701                 break;
31702         }
31703         
31704         
31705         this.win.focus();
31706         this.execCmd(cmd, value);
31707         this.owner.fireEvent('editorevent', this);
31708         //this.updateToolbar();
31709         this.owner.deferFocus();
31710     },
31711
31712     /**
31713      * Executes a Midas editor command directly on the editor document.
31714      * For visual commands, you should use {@link #relayCmd} instead.
31715      * <b>This should only be called after the editor is initialized.</b>
31716      * @param {String} cmd The Midas command
31717      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
31718      */
31719     execCmd : function(cmd, value){
31720         this.doc.execCommand(cmd, false, value === undefined ? null : value);
31721         this.syncValue();
31722     },
31723  
31724  
31725    
31726     /**
31727      * Inserts the passed text at the current cursor position. Note: the editor must be initialized and activated
31728      * to insert tRoo.
31729      * @param {String} text | dom node.. 
31730      */
31731     insertAtCursor : function(text)
31732     {
31733         
31734         if(!this.activated){
31735             return;
31736         }
31737          
31738         if(Roo.isGecko || Roo.isOpera || Roo.isSafari){
31739             this.win.focus();
31740             
31741             
31742             // from jquery ui (MIT licenced)
31743             var range, node;
31744             var win = this.win;
31745             
31746             if (win.getSelection && win.getSelection().getRangeAt) {
31747                 
31748                 // delete the existing?
31749                 
31750                 this.createRange(this.getSelection()).deleteContents();
31751                 range = win.getSelection().getRangeAt(0);
31752                 node = typeof(text) == 'string' ? range.createContextualFragment(text) : text;
31753                 range.insertNode(node);
31754                 range = range.cloneRange();
31755                 range.collapse(false);
31756                  
31757                 win.getSelection().removeAllRanges();
31758                 win.getSelection().addRange(range);
31759                 
31760                 
31761                 
31762             } else if (win.document.selection && win.document.selection.createRange) {
31763                 // no firefox support
31764                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
31765                 win.document.selection.createRange().pasteHTML(txt);
31766             
31767             } else {
31768                 // no firefox support
31769                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
31770                 this.execCmd('InsertHTML', txt);
31771             } 
31772             this.syncValue();
31773             
31774             this.deferFocus();
31775         }
31776     },
31777  // private
31778     mozKeyPress : function(e){
31779         if(e.ctrlKey){
31780             var c = e.getCharCode(), cmd;
31781           
31782             if(c > 0){
31783                 c = String.fromCharCode(c).toLowerCase();
31784                 switch(c){
31785                     case 'b':
31786                         cmd = 'bold';
31787                         break;
31788                     case 'i':
31789                         cmd = 'italic';
31790                         break;
31791                     
31792                     case 'u':
31793                         cmd = 'underline';
31794                         break;
31795                     
31796                     //case 'v':
31797                       //  this.cleanUpPaste.defer(100, this);
31798                       //  return;
31799                         
31800                 }
31801                 if(cmd){
31802                     
31803                     this.relayCmd(cmd);
31804                     //this.win.focus();
31805                     //this.execCmd(cmd);
31806                     //this.deferFocus();
31807                     e.preventDefault();
31808                 }
31809                 
31810             }
31811         }
31812     },
31813
31814     // private
31815     fixKeys : function(){ // load time branching for fastest keydown performance
31816         
31817         
31818         if(Roo.isIE){
31819             return function(e){
31820                 var k = e.getKey(), r;
31821                 if(k == e.TAB){
31822                     e.stopEvent();
31823                     r = this.doc.selection.createRange();
31824                     if(r){
31825                         r.collapse(true);
31826                         r.pasteHTML('&#160;&#160;&#160;&#160;');
31827                         this.deferFocus();
31828                     }
31829                     return;
31830                 }
31831                 /// this is handled by Roo.htmleditor.KeyEnter
31832                  /*
31833                 if(k == e.ENTER){
31834                     r = this.doc.selection.createRange();
31835                     if(r){
31836                         var target = r.parentElement();
31837                         if(!target || target.tagName.toLowerCase() != 'li'){
31838                             e.stopEvent();
31839                             r.pasteHTML('<br/>');
31840                             r.collapse(false);
31841                             r.select();
31842                         }
31843                     }
31844                 }
31845                 */
31846                 //if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
31847                 //    this.cleanUpPaste.defer(100, this);
31848                 //    return;
31849                 //}
31850                 
31851                 
31852             };
31853         }else if(Roo.isOpera){
31854             return function(e){
31855                 var k = e.getKey();
31856                 if(k == e.TAB){
31857                     e.stopEvent();
31858                     this.win.focus();
31859                     this.execCmd('InsertHTML','&#160;&#160;&#160;&#160;');
31860                     this.deferFocus();
31861                 }
31862                
31863                 //if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
31864                 //    this.cleanUpPaste.defer(100, this);
31865                  //   return;
31866                 //}
31867                 
31868             };
31869         }else if(Roo.isSafari){
31870             return function(e){
31871                 var k = e.getKey();
31872                 
31873                 if(k == e.TAB){
31874                     e.stopEvent();
31875                     this.execCmd('InsertText','\t');
31876                     this.deferFocus();
31877                     return;
31878                 }
31879                  this.mozKeyPress(e);
31880                 
31881                //if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
31882                  //   this.cleanUpPaste.defer(100, this);
31883                  //   return;
31884                // }
31885                 
31886              };
31887         }
31888     }(),
31889     
31890     getAllAncestors: function()
31891     {
31892         var p = this.getSelectedNode();
31893         var a = [];
31894         if (!p) {
31895             a.push(p); // push blank onto stack..
31896             p = this.getParentElement();
31897         }
31898         
31899         
31900         while (p && (p.nodeType == 1) && (p.tagName.toLowerCase() != 'body')) {
31901             a.push(p);
31902             p = p.parentNode;
31903         }
31904         a.push(this.doc.body);
31905         return a;
31906     },
31907     lastSel : false,
31908     lastSelNode : false,
31909     
31910     
31911     getSelection : function() 
31912     {
31913         this.assignDocWin();
31914         return Roo.lib.Selection.wrap(Roo.isIE ? this.doc.selection : this.win.getSelection(), this.doc);
31915     },
31916     /**
31917      * Select a dom node
31918      * @param {DomElement} node the node to select
31919      */
31920     selectNode : function(node, collapse)
31921     {
31922         var nodeRange = node.ownerDocument.createRange();
31923         try {
31924             nodeRange.selectNode(node);
31925         } catch (e) {
31926             nodeRange.selectNodeContents(node);
31927         }
31928         if (collapse === true) {
31929             nodeRange.collapse(true);
31930         }
31931         //
31932         var s = this.win.getSelection();
31933         s.removeAllRanges();
31934         s.addRange(nodeRange);
31935     },
31936     
31937     getSelectedNode: function() 
31938     {
31939         // this may only work on Gecko!!!
31940         
31941         // should we cache this!!!!
31942         
31943          
31944          
31945         var range = this.createRange(this.getSelection()).cloneRange();
31946         
31947         if (Roo.isIE) {
31948             var parent = range.parentElement();
31949             while (true) {
31950                 var testRange = range.duplicate();
31951                 testRange.moveToElementText(parent);
31952                 if (testRange.inRange(range)) {
31953                     break;
31954                 }
31955                 if ((parent.nodeType != 1) || (parent.tagName.toLowerCase() == 'body')) {
31956                     break;
31957                 }
31958                 parent = parent.parentElement;
31959             }
31960             return parent;
31961         }
31962         
31963         // is ancestor a text element.
31964         var ac =  range.commonAncestorContainer;
31965         if (ac.nodeType == 3) {
31966             ac = ac.parentNode;
31967         }
31968         
31969         var ar = ac.childNodes;
31970          
31971         var nodes = [];
31972         var other_nodes = [];
31973         var has_other_nodes = false;
31974         for (var i=0;i<ar.length;i++) {
31975             if ((ar[i].nodeType == 3) && (!ar[i].data.length)) { // empty text ? 
31976                 continue;
31977             }
31978             // fullly contained node.
31979             
31980             if (this.rangeIntersectsNode(range,ar[i]) && this.rangeCompareNode(range,ar[i]) == 3) {
31981                 nodes.push(ar[i]);
31982                 continue;
31983             }
31984             
31985             // probably selected..
31986             if ((ar[i].nodeType == 1) && this.rangeIntersectsNode(range,ar[i]) && (this.rangeCompareNode(range,ar[i]) > 0)) {
31987                 other_nodes.push(ar[i]);
31988                 continue;
31989             }
31990             // outer..
31991             if (!this.rangeIntersectsNode(range,ar[i])|| (this.rangeCompareNode(range,ar[i]) == 0))  {
31992                 continue;
31993             }
31994             
31995             
31996             has_other_nodes = true;
31997         }
31998         if (!nodes.length && other_nodes.length) {
31999             nodes= other_nodes;
32000         }
32001         if (has_other_nodes || !nodes.length || (nodes.length > 1)) {
32002             return false;
32003         }
32004         
32005         return nodes[0];
32006     },
32007     
32008     
32009     createRange: function(sel)
32010     {
32011         // this has strange effects when using with 
32012         // top toolbar - not sure if it's a great idea.
32013         //this.editor.contentWindow.focus();
32014         if (typeof sel != "undefined") {
32015             try {
32016                 return sel.getRangeAt ? sel.getRangeAt(0) : sel.createRange();
32017             } catch(e) {
32018                 return this.doc.createRange();
32019             }
32020         } else {
32021             return this.doc.createRange();
32022         }
32023     },
32024     getParentElement: function()
32025     {
32026         
32027         this.assignDocWin();
32028         var sel = Roo.isIE ? this.doc.selection : this.win.getSelection();
32029         
32030         var range = this.createRange(sel);
32031          
32032         try {
32033             var p = range.commonAncestorContainer;
32034             while (p.nodeType == 3) { // text node
32035                 p = p.parentNode;
32036             }
32037             return p;
32038         } catch (e) {
32039             return null;
32040         }
32041     
32042     },
32043     /***
32044      *
32045      * Range intersection.. the hard stuff...
32046      *  '-1' = before
32047      *  '0' = hits..
32048      *  '1' = after.
32049      *         [ -- selected range --- ]
32050      *   [fail]                        [fail]
32051      *
32052      *    basically..
32053      *      if end is before start or  hits it. fail.
32054      *      if start is after end or hits it fail.
32055      *
32056      *   if either hits (but other is outside. - then it's not 
32057      *   
32058      *    
32059      **/
32060     
32061     
32062     // @see http://www.thismuchiknow.co.uk/?p=64.
32063     rangeIntersectsNode : function(range, node)
32064     {
32065         var nodeRange = node.ownerDocument.createRange();
32066         try {
32067             nodeRange.selectNode(node);
32068         } catch (e) {
32069             nodeRange.selectNodeContents(node);
32070         }
32071     
32072         var rangeStartRange = range.cloneRange();
32073         rangeStartRange.collapse(true);
32074     
32075         var rangeEndRange = range.cloneRange();
32076         rangeEndRange.collapse(false);
32077     
32078         var nodeStartRange = nodeRange.cloneRange();
32079         nodeStartRange.collapse(true);
32080     
32081         var nodeEndRange = nodeRange.cloneRange();
32082         nodeEndRange.collapse(false);
32083     
32084         return rangeStartRange.compareBoundaryPoints(
32085                  Range.START_TO_START, nodeEndRange) == -1 &&
32086                rangeEndRange.compareBoundaryPoints(
32087                  Range.START_TO_START, nodeStartRange) == 1;
32088         
32089          
32090     },
32091     rangeCompareNode : function(range, node)
32092     {
32093         var nodeRange = node.ownerDocument.createRange();
32094         try {
32095             nodeRange.selectNode(node);
32096         } catch (e) {
32097             nodeRange.selectNodeContents(node);
32098         }
32099         
32100         
32101         range.collapse(true);
32102     
32103         nodeRange.collapse(true);
32104      
32105         var ss = range.compareBoundaryPoints( Range.START_TO_START, nodeRange);
32106         var ee = range.compareBoundaryPoints(  Range.END_TO_END, nodeRange);
32107          
32108         //Roo.log(node.tagName + ': ss='+ss +', ee='+ee)
32109         
32110         var nodeIsBefore   =  ss == 1;
32111         var nodeIsAfter    = ee == -1;
32112         
32113         if (nodeIsBefore && nodeIsAfter) {
32114             return 0; // outer
32115         }
32116         if (!nodeIsBefore && nodeIsAfter) {
32117             return 1; //right trailed.
32118         }
32119         
32120         if (nodeIsBefore && !nodeIsAfter) {
32121             return 2;  // left trailed.
32122         }
32123         // fully contined.
32124         return 3;
32125     },
32126  
32127     cleanWordChars : function(input) {// change the chars to hex code
32128         
32129        var swapCodes  = [ 
32130             [    8211, "&#8211;" ], 
32131             [    8212, "&#8212;" ], 
32132             [    8216,  "'" ],  
32133             [    8217, "'" ],  
32134             [    8220, '"' ],  
32135             [    8221, '"' ],  
32136             [    8226, "*" ],  
32137             [    8230, "..." ]
32138         ]; 
32139         var output = input;
32140         Roo.each(swapCodes, function(sw) { 
32141             var swapper = new RegExp("\\u" + sw[0].toString(16), "g"); // hex codes
32142             
32143             output = output.replace(swapper, sw[1]);
32144         });
32145         
32146         return output;
32147     },
32148     
32149      
32150     
32151         
32152     
32153     cleanUpChild : function (node)
32154     {
32155         
32156         new Roo.htmleditor.FilterComment({node : node});
32157         new Roo.htmleditor.FilterAttributes({
32158                 node : node,
32159                 attrib_black : this.ablack,
32160                 attrib_clean : this.aclean,
32161                 style_white : this.cwhite,
32162                 style_black : this.cblack
32163         });
32164         new Roo.htmleditor.FilterBlack({ node : node, tag : this.black});
32165         new Roo.htmleditor.FilterKeepChildren({node : node, tag : this.tag_remove} );
32166          
32167         
32168     },
32169     
32170     /**
32171      * Clean up MS wordisms...
32172      * @deprecated - use filter directly
32173      */
32174     cleanWord : function(node)
32175     {
32176         new Roo.htmleditor.FilterWord({ node : node ? node : this.doc.body });
32177         new Roo.htmleditor.FilterKeepChildren({node : node ? node : this.doc.body, tag : [ 'FONT', ':' ]} );
32178         
32179     },
32180    
32181     
32182     /**
32183
32184      * @deprecated - use filters
32185      */
32186     cleanTableWidths : function(node)
32187     {
32188         new Roo.htmleditor.FilterTableWidth({ node : node ? node : this.doc.body});
32189         
32190  
32191     },
32192     
32193      
32194         
32195     applyBlacklists : function()
32196     {
32197         var w = typeof(this.owner.white) != 'undefined' && this.owner.white ? this.owner.white  : [];
32198         var b = typeof(this.owner.black) != 'undefined' && this.owner.black ? this.owner.black :  [];
32199         
32200         this.aclean = typeof(this.owner.aclean) != 'undefined' && this.owner.aclean ? this.owner.aclean :  Roo.HtmlEditorCore.aclean;
32201         this.ablack = typeof(this.owner.ablack) != 'undefined' && this.owner.ablack ? this.owner.ablack :  Roo.HtmlEditorCore.ablack;
32202         this.tag_remove = typeof(this.owner.tag_remove) != 'undefined' && this.owner.tag_remove ? this.owner.tag_remove :  Roo.HtmlEditorCore.tag_remove;
32203         
32204         this.white = [];
32205         this.black = [];
32206         Roo.each(Roo.HtmlEditorCore.white, function(tag) {
32207             if (b.indexOf(tag) > -1) {
32208                 return;
32209             }
32210             this.white.push(tag);
32211             
32212         }, this);
32213         
32214         Roo.each(w, function(tag) {
32215             if (b.indexOf(tag) > -1) {
32216                 return;
32217             }
32218             if (this.white.indexOf(tag) > -1) {
32219                 return;
32220             }
32221             this.white.push(tag);
32222             
32223         }, this);
32224         
32225         
32226         Roo.each(Roo.HtmlEditorCore.black, function(tag) {
32227             if (w.indexOf(tag) > -1) {
32228                 return;
32229             }
32230             this.black.push(tag);
32231             
32232         }, this);
32233         
32234         Roo.each(b, function(tag) {
32235             if (w.indexOf(tag) > -1) {
32236                 return;
32237             }
32238             if (this.black.indexOf(tag) > -1) {
32239                 return;
32240             }
32241             this.black.push(tag);
32242             
32243         }, this);
32244         
32245         
32246         w = typeof(this.owner.cwhite) != 'undefined' && this.owner.cwhite ? this.owner.cwhite  : [];
32247         b = typeof(this.owner.cblack) != 'undefined' && this.owner.cblack ? this.owner.cblack :  [];
32248         
32249         this.cwhite = [];
32250         this.cblack = [];
32251         Roo.each(Roo.HtmlEditorCore.cwhite, function(tag) {
32252             if (b.indexOf(tag) > -1) {
32253                 return;
32254             }
32255             this.cwhite.push(tag);
32256             
32257         }, this);
32258         
32259         Roo.each(w, function(tag) {
32260             if (b.indexOf(tag) > -1) {
32261                 return;
32262             }
32263             if (this.cwhite.indexOf(tag) > -1) {
32264                 return;
32265             }
32266             this.cwhite.push(tag);
32267             
32268         }, this);
32269         
32270         
32271         Roo.each(Roo.HtmlEditorCore.cblack, function(tag) {
32272             if (w.indexOf(tag) > -1) {
32273                 return;
32274             }
32275             this.cblack.push(tag);
32276             
32277         }, this);
32278         
32279         Roo.each(b, function(tag) {
32280             if (w.indexOf(tag) > -1) {
32281                 return;
32282             }
32283             if (this.cblack.indexOf(tag) > -1) {
32284                 return;
32285             }
32286             this.cblack.push(tag);
32287             
32288         }, this);
32289     },
32290     
32291     setStylesheets : function(stylesheets)
32292     {
32293         if(typeof(stylesheets) == 'string'){
32294             Roo.get(this.iframe.contentDocument.head).createChild({
32295                 tag : 'link',
32296                 rel : 'stylesheet',
32297                 type : 'text/css',
32298                 href : stylesheets
32299             });
32300             
32301             return;
32302         }
32303         var _this = this;
32304      
32305         Roo.each(stylesheets, function(s) {
32306             if(!s.length){
32307                 return;
32308             }
32309             
32310             Roo.get(_this.iframe.contentDocument.head).createChild({
32311                 tag : 'link',
32312                 rel : 'stylesheet',
32313                 type : 'text/css',
32314                 href : s
32315             });
32316         });
32317
32318         
32319     },
32320     
32321     
32322     updateLanguage : function()
32323     {
32324         if (!this.iframe || !this.iframe.contentDocument) {
32325             return;
32326         }
32327         Roo.get(this.iframe.contentDocument.body).attr("lang", this.language);
32328     },
32329     
32330     
32331     removeStylesheets : function()
32332     {
32333         var _this = this;
32334         
32335         Roo.each(Roo.get(_this.iframe.contentDocument.head).select('link[rel=stylesheet]', true).elements, function(s){
32336             s.remove();
32337         });
32338     },
32339     
32340     setStyle : function(style)
32341     {
32342         Roo.get(this.iframe.contentDocument.head).createChild({
32343             tag : 'style',
32344             type : 'text/css',
32345             html : style
32346         });
32347
32348         return;
32349     }
32350     
32351     // hide stuff that is not compatible
32352     /**
32353      * @event blur
32354      * @hide
32355      */
32356     /**
32357      * @event change
32358      * @hide
32359      */
32360     /**
32361      * @event focus
32362      * @hide
32363      */
32364     /**
32365      * @event specialkey
32366      * @hide
32367      */
32368     /**
32369      * @cfg {String} fieldClass @hide
32370      */
32371     /**
32372      * @cfg {String} focusClass @hide
32373      */
32374     /**
32375      * @cfg {String} autoCreate @hide
32376      */
32377     /**
32378      * @cfg {String} inputType @hide
32379      */
32380     /**
32381      * @cfg {String} invalidClass @hide
32382      */
32383     /**
32384      * @cfg {String} invalidText @hide
32385      */
32386     /**
32387      * @cfg {String} msgFx @hide
32388      */
32389     /**
32390      * @cfg {String} validateOnBlur @hide
32391      */
32392 });
32393
32394 Roo.HtmlEditorCore.white = [
32395         'AREA', 'BR', 'IMG', 'INPUT', 'HR', 'WBR',
32396         
32397        'ADDRESS', 'BLOCKQUOTE', 'CENTER', 'DD',      'DIR',       'DIV', 
32398        'DL',      'DT',         'H1',     'H2',      'H3',        'H4', 
32399        'H5',      'H6',         'HR',     'ISINDEX', 'LISTING',   'MARQUEE', 
32400        'MENU',    'MULTICOL',   'OL',     'P',       'PLAINTEXT', 'PRE', 
32401        'TABLE',   'UL',         'XMP', 
32402        
32403        'CAPTION', 'COL', 'COLGROUP', 'TBODY', 'TD', 'TFOOT', 'TH', 
32404       'THEAD',   'TR', 
32405      
32406       'DIR', 'MENU', 'OL', 'UL', 'DL',
32407        
32408       'EMBED',  'OBJECT'
32409 ];
32410
32411
32412 Roo.HtmlEditorCore.black = [
32413     //    'embed',  'object', // enable - backend responsiblity to clean thiese
32414         'APPLET', // 
32415         'BASE',   'BASEFONT', 'BGSOUND', 'BLINK',  'BODY', 
32416         'FRAME',  'FRAMESET', 'HEAD',    'HTML',   'ILAYER', 
32417         'IFRAME', 'LAYER',  'LINK',     'META',    'OBJECT',   
32418         'SCRIPT', 'STYLE' ,'TITLE',  'XML',
32419         //'FONT' // CLEAN LATER..
32420         'COLGROUP', 'COL'   // messy tables.
32421         
32422         
32423 ];
32424 Roo.HtmlEditorCore.clean = [ // ?? needed???
32425      'SCRIPT', 'STYLE', 'TITLE', 'XML'
32426 ];
32427 Roo.HtmlEditorCore.tag_remove = [
32428     'FONT', 'TBODY'  
32429 ];
32430 // attributes..
32431
32432 Roo.HtmlEditorCore.ablack = [
32433     'on'
32434 ];
32435     
32436 Roo.HtmlEditorCore.aclean = [ 
32437     'action', 'background', 'codebase', 'dynsrc', 'href', 'lowsrc' 
32438 ];
32439
32440 // protocols..
32441 Roo.HtmlEditorCore.pwhite= [
32442         'http',  'https',  'mailto'
32443 ];
32444
32445 // white listed style attributes.
32446 Roo.HtmlEditorCore.cwhite= [
32447       //  'text-align', /// default is to allow most things..
32448       
32449          
32450 //        'font-size'//??
32451 ];
32452
32453 // black listed style attributes.
32454 Roo.HtmlEditorCore.cblack= [
32455       //  'font-size' -- this can be set by the project 
32456 ];
32457
32458
32459
32460
32461     /*
32462  * - LGPL
32463  *
32464  * HtmlEditor
32465  * 
32466  */
32467
32468 /**
32469  * @class Roo.bootstrap.form.HtmlEditor
32470  * @extends Roo.bootstrap.form.TextArea
32471  * Bootstrap HtmlEditor class
32472
32473  * @constructor
32474  * Create a new HtmlEditor
32475  * @param {Object} config The config object
32476  */
32477
32478 Roo.bootstrap.form.HtmlEditor = function(config){
32479
32480     this.addEvents({
32481             /**
32482              * @event initialize
32483              * Fires when the editor is fully initialized (including the iframe)
32484              * @param {Roo.bootstrap.form.HtmlEditor} this
32485              */
32486             initialize: true,
32487             /**
32488              * @event activate
32489              * Fires when the editor is first receives the focus. Any insertion must wait
32490              * until after this event.
32491              * @param {Roo.bootstrap.form.HtmlEditor} this
32492              */
32493             activate: true,
32494              /**
32495              * @event beforesync
32496              * Fires before the textarea is updated with content from the editor iframe. Return false
32497              * to cancel the sync.
32498              * @param {Roo.bootstrap.form.HtmlEditor} this
32499              * @param {String} html
32500              */
32501             beforesync: true,
32502              /**
32503              * @event beforepush
32504              * Fires before the iframe editor is updated with content from the textarea. Return false
32505              * to cancel the push.
32506              * @param {Roo.bootstrap.form.HtmlEditor} this
32507              * @param {String} html
32508              */
32509             beforepush: true,
32510              /**
32511              * @event sync
32512              * Fires when the textarea is updated with content from the editor iframe.
32513              * @param {Roo.bootstrap.form.HtmlEditor} this
32514              * @param {String} html
32515              */
32516             sync: true,
32517              /**
32518              * @event push
32519              * Fires when the iframe editor is updated with content from the textarea.
32520              * @param {Roo.bootstrap.form.HtmlEditor} this
32521              * @param {String} html
32522              */
32523             push: true,
32524              /**
32525              * @event editmodechange
32526              * Fires when the editor switches edit modes
32527              * @param {Roo.bootstrap.form.HtmlEditor} this
32528              * @param {Boolean} sourceEdit True if source edit, false if standard editing.
32529              */
32530             editmodechange: true,
32531             /**
32532              * @event editorevent
32533              * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
32534              * @param {Roo.bootstrap.form.HtmlEditor} this
32535              */
32536             editorevent: true,
32537             /**
32538              * @event firstfocus
32539              * Fires when on first focus - needed by toolbars..
32540              * @param {Roo.bootstrap.form.HtmlEditor} this
32541              */
32542             firstfocus: true,
32543             /**
32544              * @event autosave
32545              * Auto save the htmlEditor value as a file into Events
32546              * @param {Roo.bootstrap.form.HtmlEditor} this
32547              */
32548             autosave: true,
32549             /**
32550              * @event savedpreview
32551              * preview the saved version of htmlEditor
32552              * @param {Roo.bootstrap.form.HtmlEditor} this
32553              */
32554             savedpreview: true,
32555              /**
32556             * @event stylesheetsclick
32557             * Fires when press the Sytlesheets button
32558             * @param {Roo.HtmlEditorCore} this
32559             */
32560             stylesheetsclick: true,
32561             /**
32562             * @event paste
32563             * Fires when press user pastes into the editor
32564             * @param {Roo.HtmlEditorCore} this
32565             */
32566             paste: true,
32567             /**
32568             * @event imageadd
32569             * Fires when on any editor when an image is added (excluding paste)
32570             * @param {Roo.bootstrap.form.HtmlEditor} this
32571             */
32572            imageadd: true ,
32573             /**
32574             * @event imageupdated
32575             * Fires when on any editor when an image is changed (excluding paste)
32576             * @param {Roo.bootstrap.form.HtmlEditor} this
32577             * @param {HTMLElement} img could also be a figure if blocks are enabled
32578             */
32579            imageupdate: true ,
32580            /**
32581             * @event imagedelete
32582             * Fires when on any editor when an image is deleted
32583             * @param {Roo.bootstrap.form.HtmlEditor} this
32584             * @param {HTMLElement} img could also be a figure if blocks are enabled
32585             */
32586            imagedelete: true  
32587     });
32588     Roo.bootstrap.form.HtmlEditor.superclass.constructor.call(this, config);
32589     if (!this.toolbars) {
32590         this.toolbars = [];
32591     }
32592     
32593     this.editorcore = new Roo.HtmlEditorCore(Roo.apply({ owner : this} , config));
32594     
32595 };
32596
32597
32598 Roo.extend(Roo.bootstrap.form.HtmlEditor, Roo.bootstrap.form.TextArea,  {
32599     
32600     
32601       /**
32602      * @cfg {Array|boolean} toolbars Array of toolbars, or names of toolbars. - true for standard, and false for none.
32603      */
32604     toolbars : true,
32605     
32606      /**
32607     * @cfg {Array} buttons Array of toolbar's buttons. - defaults to empty
32608     */
32609     btns : [],
32610    
32611      /**
32612      * @cfg {String} resize  (none|both|horizontal|vertical) - css resize of element
32613      */
32614     resize : false,
32615      /**
32616      * @cfg {Number} height (in pixels)
32617      */   
32618     height: 300,
32619    /**
32620      * @cfg {Number} width (in pixels)
32621      */   
32622     width: false,
32623     
32624     /**
32625      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
32626      * 
32627      */
32628     stylesheets: false,
32629     
32630     // id of frame..
32631     frameId: false,
32632     
32633     // private properties
32634     validationEvent : false,
32635     deferHeight: true,
32636     initialized : false,
32637     activated : false,
32638     
32639     onFocus : Roo.emptyFn,
32640     iframePad:3,
32641     hideMode:'offsets',
32642     
32643     tbContainer : false,
32644     
32645     bodyCls : '',
32646     
32647     toolbarContainer :function() {
32648         return this.wrap.select('.x-html-editor-tb',true).first();
32649     },
32650
32651     /**
32652      * Protected method that will not generally be called directly. It
32653      * is called when the editor creates its toolbar. Override this method if you need to
32654      * add custom toolbar buttons.
32655      * @param {HtmlEditor} editor
32656      */
32657     createToolbar : function()
32658     {
32659         //Roo.log('renewing');
32660         //Roo.log("create toolbars");
32661         if (this.toolbars === false) {
32662             return;
32663         }
32664         if (this.toolbars === true) {
32665             this.toolbars = [ 'Standard' ];
32666         }
32667         
32668         var ar = Array.from(this.toolbars);
32669         this.toolbars = [];
32670         ar.forEach(function(t,i) {
32671             if (typeof(t) == 'string') {
32672                 t = {
32673                     xtype : t
32674                 };
32675             }
32676             if (typeof(t) == 'object' && typeof(t.xtype) == 'string') {
32677                 t.editor = this;
32678                 t.xns = t.xns || Roo.bootstrap.form.HtmlEditorToolbar;
32679                 t = Roo.factory(t);
32680             }
32681             this.toolbars[i] = t;
32682             this.toolbars[i].render(this.toolbarContainer());
32683         }, this);
32684         
32685         
32686     },
32687
32688      
32689     // private
32690     onRender : function(ct, position)
32691     {
32692        // Roo.log("Call onRender: " + this.xtype);
32693         var _t = this;
32694         Roo.bootstrap.form.HtmlEditor.superclass.onRender.call(this, ct, position);
32695       
32696         this.wrap = this.inputEl().wrap({
32697             cls:'x-html-editor-wrap', cn:{cls:'x-html-editor-tb'}
32698         });
32699         
32700         this.editorcore.onRender(ct, position);
32701          
32702          
32703         this.createToolbar(this);
32704        
32705         
32706           
32707         
32708     },
32709
32710     // private
32711     onResize : function(w, h)
32712     {
32713         Roo.log('resize: ' +w + ',' + h );
32714         Roo.bootstrap.form.HtmlEditor.superclass.onResize.apply(this, arguments);
32715         var ew = false;
32716         var eh = false;
32717         
32718         if(this.inputEl() ){
32719             if(typeof w == 'number'){
32720                 var aw = w - this.wrap.getFrameWidth('lr');
32721                 this.inputEl().setWidth(this.adjustWidth('textarea', aw));
32722                 ew = aw;
32723             }
32724             if(typeof h == 'number'){
32725                  var tbh = -11;  // fixme it needs to tool bar size!
32726                 for (var i =0; i < this.toolbars.length;i++) {
32727                     // fixme - ask toolbars for heights?
32728                     tbh += this.toolbars[i].el.getHeight();
32729                     //if (this.toolbars[i].footer) {
32730                     //    tbh += this.toolbars[i].footer.el.getHeight();
32731                     //}
32732                 }
32733               
32734                 
32735                 
32736                 
32737                 
32738                 var ah = h - this.wrap.getFrameWidth('tb') - tbh;// this.tb.el.getHeight();
32739                 ah -= 5; // knock a few pixes off for look..
32740                 this.inputEl().setHeight(this.adjustWidth('textarea', ah));
32741                 var eh = ah;
32742             }
32743         }
32744         Roo.log('onResize:' + [w,h,ew,eh].join(',') );
32745         this.editorcore.onResize(ew,eh);
32746         
32747     },
32748
32749     /**
32750      * Toggles the editor between standard and source edit mode.
32751      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
32752      */
32753     toggleSourceEdit : function(sourceEditMode)
32754     {
32755         this.editorcore.toggleSourceEdit(sourceEditMode);
32756         
32757         if(this.editorcore.sourceEditMode){
32758             Roo.log('editor - showing textarea');
32759             
32760 //            Roo.log('in');
32761 //            Roo.log(this.syncValue());
32762             this.syncValue();
32763             this.inputEl().removeClass(['hide', 'x-hidden']);
32764             this.inputEl().dom.removeAttribute('tabIndex');
32765             this.inputEl().focus();
32766         }else{
32767             Roo.log('editor - hiding textarea');
32768 //            Roo.log('out')
32769 //            Roo.log(this.pushValue()); 
32770             this.pushValue();
32771             
32772             this.inputEl().addClass(['hide', 'x-hidden']);
32773             this.inputEl().dom.setAttribute('tabIndex', -1);
32774             //this.deferFocus();
32775         }
32776          
32777         //if(this.resizable){
32778         //    this.setSize(this.wrap.getSize());
32779         //}
32780         
32781         this.fireEvent('editmodechange', this, this.editorcore.sourceEditMode);
32782     },
32783  
32784     // private (for BoxComponent)
32785     adjustSize : Roo.BoxComponent.prototype.adjustSize,
32786
32787     // private (for BoxComponent)
32788     getResizeEl : function(){
32789         return this.wrap;
32790     },
32791
32792     // private (for BoxComponent)
32793     getPositionEl : function(){
32794         return this.wrap;
32795     },
32796
32797     // private
32798     initEvents : function(){
32799         this.originalValue = this.getValue();
32800     },
32801
32802 //    /**
32803 //     * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
32804 //     * @method
32805 //     */
32806 //    markInvalid : Roo.emptyFn,
32807 //    /**
32808 //     * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
32809 //     * @method
32810 //     */
32811 //    clearInvalid : Roo.emptyFn,
32812
32813     setValue : function(v){
32814         Roo.bootstrap.form.HtmlEditor.superclass.setValue.call(this, v);
32815         this.editorcore.pushValue();
32816     },
32817
32818      
32819     // private
32820     deferFocus : function(){
32821         this.focus.defer(10, this);
32822     },
32823
32824     // doc'ed in Field
32825     focus : function(){
32826         this.editorcore.focus();
32827         
32828     },
32829       
32830
32831     // private
32832     onDestroy : function(){
32833         
32834         
32835         
32836         if(this.rendered){
32837             
32838             for (var i =0; i < this.toolbars.length;i++) {
32839                 // fixme - ask toolbars for heights?
32840                 this.toolbars[i].onDestroy();
32841             }
32842             
32843             this.wrap.dom.innerHTML = '';
32844             this.wrap.remove();
32845         }
32846     },
32847
32848     // private
32849     onFirstFocus : function(){
32850         //Roo.log("onFirstFocus");
32851         this.editorcore.onFirstFocus();
32852          for (var i =0; i < this.toolbars.length;i++) {
32853             this.toolbars[i].onFirstFocus();
32854         }
32855         
32856     },
32857     
32858     // private
32859     syncValue : function()
32860     {   
32861         this.editorcore.syncValue();
32862     },
32863     
32864     pushValue : function()
32865     {   
32866         this.editorcore.pushValue();
32867     }
32868      
32869     
32870     // hide stuff that is not compatible
32871     /**
32872      * @event blur
32873      * @hide
32874      */
32875     /**
32876      * @event change
32877      * @hide
32878      */
32879     /**
32880      * @event focus
32881      * @hide
32882      */
32883     /**
32884      * @event specialkey
32885      * @hide
32886      */
32887     /**
32888      * @cfg {String} fieldClass @hide
32889      */
32890     /**
32891      * @cfg {String} focusClass @hide
32892      */
32893     /**
32894      * @cfg {String} autoCreate @hide
32895      */
32896     /**
32897      * @cfg {String} inputType @hide
32898      */
32899      
32900     /**
32901      * @cfg {String} invalidText @hide
32902      */
32903     /**
32904      * @cfg {String} msgFx @hide
32905      */
32906     /**
32907      * @cfg {String} validateOnBlur @hide
32908      */
32909 });
32910  
32911     
32912    
32913    
32914    
32915       
32916 /**
32917  * @class Roo.bootstrap.form.HtmlEditorToolbar.Standard
32918  * @parent Roo.bootstrap.form.HtmlEditor
32919  * @extends Roo.bootstrap.nav.Simplebar
32920  * Basic Toolbar
32921  * 
32922  * @example
32923  * Usage:
32924  *
32925  new Roo.bootstrap.form.HtmlEditor({
32926     ....
32927     toolbars : [
32928         new Roo.bootstrap.form.HtmlEditorToolbar.Standard({
32929             disable : { fonts: 1 , format: 1, ..., ... , ...],
32930             btns : [ .... ]
32931         })
32932     }
32933      
32934  * 
32935  * @cfg {Object} disable List of elements to disable..
32936  * @cfg {Array} btns List of additional buttons.
32937  * 
32938  * 
32939  * NEEDS Extra CSS? 
32940  * .x-html-editor-tb .x-edit-none .x-btn-text { background: none; }
32941  */
32942  
32943 Roo.bootstrap.form.HtmlEditorToolbar.Standard = function(config)
32944 {
32945     
32946     Roo.apply(this, config);
32947     
32948     // default disabled, based on 'good practice'..
32949     this.disable = this.disable || {};
32950     Roo.applyIf(this.disable, {
32951         fontSize : true,
32952         colors : true,
32953         specialElements : true
32954     });
32955     Roo.bootstrap.form.HtmlEditorToolbar.Standard.superclass.constructor.call(this, config);
32956     
32957     this.editor = config.editor;
32958     this.editorcore = config.editor.editorcore;
32959     
32960     this.buttons   = new Roo.util.MixedCollection(false, function(o) { return o.btnid; });
32961     
32962     //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
32963     // dont call parent... till later.
32964 }
32965 Roo.extend(Roo.bootstrap.form.HtmlEditorToolbar.Standard, Roo.bootstrap.nav.Simplebar,  {
32966      
32967     bar : true,
32968     
32969     editor : false,
32970     editorcore : false,
32971     
32972     
32973     formats : [
32974         "p" ,  
32975         "h1","h2","h3","h4","h5","h6", 
32976         "pre", "code", 
32977         "abbr", "acronym", "address", "cite", "samp", "var",
32978         'div','span'
32979     ],
32980     
32981     
32982     deleteBtn: false,
32983     
32984     onRender : function(ct, position)
32985     {
32986        // Roo.log("Call onRender: " + this.xtype);
32987         
32988        Roo.bootstrap.form.HtmlEditorToolbar.Standard.superclass.onRender.call(this, ct, position);
32989        Roo.log(this.el);
32990        this.el.dom.style.marginBottom = '0';
32991        var _this = this;
32992        var editorcore = this.editorcore;
32993        var editor= this.editor;
32994        
32995        var children = [];
32996        var btn = function(id, cmd , toggle, handler, html){
32997        
32998             var  event = toggle ? 'toggle' : 'click';
32999        
33000             var a = {
33001                 size : 'sm',
33002                 xtype: 'Button',
33003                 xns: Roo.bootstrap,
33004                 //glyphicon : id,
33005                 btnid : id,
33006                 fa: id,
33007                 cls : 'roo-html-editor-btn-' + id,
33008                 cmd : cmd, // why id || cmd
33009                 enableToggle: toggle !== false,
33010                 html : html || '',
33011                 pressed : toggle ? false : null,
33012                 listeners : {}
33013             };
33014             a.listeners[toggle ? 'toggle' : 'click'] = function() {
33015                 handler ? handler.call(_this,this) :_this.onBtnClick.call(_this, cmd ||  id);
33016             };
33017             children.push(a);
33018             return a;
33019        }
33020        
33021     //    var cb_box = function...
33022         
33023         var style = {
33024                 xtype: 'Button',
33025                 size : 'sm',
33026                 xns: Roo.bootstrap,
33027                 fa : 'font',
33028                 cls : 'roo-html-editor-font-chooser',
33029                 //html : 'submit'
33030                 menu : {
33031                     xtype: 'Menu',
33032                     xns: Roo.bootstrap,
33033                     items:  []
33034                 }
33035         };
33036         Roo.each(this.formats, function(f) {
33037             style.menu.items.push({
33038                 xtype :'MenuItem',
33039                 xns: Roo.bootstrap,
33040                 html : '<'+ f+' style="margin:2px">'+f +'</'+ f+'>',
33041                 tagname : f,
33042                 listeners : {
33043                     click : function()
33044                     {
33045                         editorcore.insertTag(this.tagname);
33046                         editor.focus();
33047                     }
33048                 }
33049                 
33050             });
33051         });
33052         children.push(style);   
33053         
33054         btn('bold',         'bold',true);
33055         btn('italic',       'italic',true);
33056         btn('underline',     'underline',true);
33057         btn('align-left',   'justifyleft',true);
33058         btn('align-center', 'justifycenter',true);
33059         btn('align-right' , 'justifyright',true);
33060         btn('link', false, true, this.onLinkClick);
33061         
33062         
33063         btn('image', false, true, this.onImageClick);
33064         btn('list','insertunorderedlist',true);
33065         btn('list-ol','insertorderedlist',true);
33066
33067         btn('pencil', false,true, function(btn){
33068                 Roo.log(this);
33069                 this.toggleSourceEdit(btn.pressed);
33070         });
33071         
33072         if (this.editor.btns.length > 0) {
33073             for (var i = 0; i<this.editor.btns.length; i++) {
33074                 children.push(this.editor.btns[i]);
33075             }
33076         }
33077         
33078         
33079          
33080         this.xtype = 'NavSimplebar'; // why?
33081         
33082         for(var i=0;i< children.length;i++) {
33083             
33084             this.buttons.add(this.addxtypeChild(children[i]));
33085             
33086         }
33087         this.buildToolbarDelete();
33088
33089         editor.on('editorevent', this.updateToolbar, this);
33090     },
33091     
33092     buildToolbarDelete : function()
33093     {
33094         
33095        /* this.addxtypeChild({
33096             xtype : 'Element',
33097             xns : Roo.bootstrap,
33098             cls : 'roo-htmleditor-fill'
33099         });
33100         */
33101         this.deleteBtn = this.addxtypeChild({
33102             size : 'sm',
33103             xtype: 'Button',
33104             xns: Roo.bootstrap,
33105             fa: 'trash',
33106             listeners : {
33107                 click : this.onDelete.createDelegate(this)
33108             }
33109         });
33110         this.deleteBtn.hide();     
33111         
33112     },
33113     
33114     onImageClick : function()
33115     {
33116         if (this.input) {
33117             this.input.un('change', this.onFileSelected, this);
33118         }
33119         this.input = Roo.get(document.body).createChild({ 
33120           tag: 'input', 
33121           type : 'file', 
33122           style : 'display:none', 
33123           multiple: 'multiple'
33124        });
33125         this.input.on('change', this.onFileSelected, this);
33126         this.input.dom.click();
33127     },
33128     
33129     onFileSelected : function(e)
33130     {
33131          e.preventDefault();
33132         
33133         if(typeof(this.input.dom.files) == 'undefined' || !this.input.dom.files.length){
33134             return;
33135         }
33136     
33137          
33138         this.addFiles(Array.prototype.slice.call(this.input.dom.files), false);
33139     },
33140     
33141     addFiles : function(far, fire_add) {
33142
33143          
33144         var editor =  this.editorcore;
33145   
33146         if (!far.length) {
33147             if (fire_add) {
33148                 this.editor.syncValue();
33149                 editor.owner.fireEvent('editorevent', editor.owner, false);
33150                 editor.owner.fireEvent('imageadd', editor.owner, false);
33151             }
33152             return;
33153         }
33154         
33155         var f = far.pop();
33156         
33157         if (!f.type.match(/^image/)) {
33158             this.addFiles(far, fire_add);
33159             return;
33160         }
33161          
33162         var sn = this.selectedNode;
33163         
33164         var bl = sn  && this.editorcore.enableBlocks ? Roo.htmleditor.Block.factory(sn) : false;
33165         
33166         
33167         var reader = new FileReader();
33168         reader.addEventListener('load', (function() {
33169             if (bl) {
33170                 bl.image_src = reader.result;
33171                 //bl.caption = f.name;
33172                 bl.updateElement(sn);
33173                 this.editor.syncValue();
33174                 editor.owner.fireEvent('editorevent', editor.owner, false);
33175                 editor.owner.fireEvent('imageupdate', editor.owner, sn);
33176                 // we only do the first file!! and replace.
33177                 return;
33178             }
33179             if (this.editorcore.enableBlocks) {
33180                 var fig = new Roo.htmleditor.BlockFigure({
33181                     image_src :  reader.result,
33182                     caption : '',
33183                     caption_display : 'none'  //default to hide captions..
33184                  });
33185                 editor.insertAtCursor(fig.toHTML());
33186                 this.addFiles(far, true);
33187                 return;
33188             }
33189             // just a standard img..
33190             if (sn && sn.tagName.toUpperCase() == 'IMG') {
33191                 sn.src = reader.result;
33192                 this.editor.syncValue();
33193                 editor.owner.fireEvent('editorevent', editor.owner, false);
33194                 editor.owner.fireEvent('imageupdate', editor.owner, sn);
33195                 return;
33196             }
33197             editor.insertAtCursor('<img src="' + reader.result +'">');
33198             this.addFiles(far, true);
33199             
33200         }).createDelegate(this));
33201         reader.readAsDataURL(f);
33202         
33203     
33204      },
33205     
33206     
33207     onBtnClick : function(id)
33208     {
33209        this.editorcore.relayCmd(id);
33210        this.editorcore.focus();
33211     },
33212     
33213     onLinkClick : function(btn) {
33214         var url = this.selectedNode && this.selectedNode.tagName.toUpperCase() == 'A' ?
33215                 this.selectedNode.getAttribute('href') : '';
33216             
33217         Roo.bootstrap.MessageBox.show({
33218             title : "Add / Edit Link URL",
33219             msg : "Enter the URL for the link",
33220             buttons: Roo.bootstrap.MessageBox.OKCANCEL,
33221             minWidth: 250,
33222             scope : this,
33223             prompt:true,
33224             multiline: false,
33225             modal : true,
33226             value : url,
33227             fn:  function(pressed, newurl) {
33228                 if (pressed != 'ok') {
33229                     this.editorcore.focus();
33230                     return;
33231                 }
33232                 if (url != '') {
33233                     this.selectedNode.setAttribute('href', newurl);
33234                     return;
33235                 }
33236                 if(newurl && newurl .match(/http(s):\/\/.+/)) {
33237                     this.editorcore.relayCmd('createlink', newurl);
33238                 }
33239                 this.editorcore.focus();
33240             }
33241         });
33242     },
33243     /**
33244      * Protected method that will not generally be called directly. It triggers
33245      * a toolbar update by reading the markup state of the current selection in the editor.
33246      */
33247     updateToolbar: function(editor ,ev, sel){
33248
33249         if(!this.editorcore.activated){
33250             this.editor.onFirstFocus(); // is this neeed?
33251             return;
33252         }
33253
33254         var btns = this.buttons; 
33255         var doc = this.editorcore.doc;
33256         var hasToggle  = false;
33257         btns.each(function(e) {
33258             if (e.enableToggle && e.cmd) {
33259                 hasToggle = hasToggle  || (['align-left', 'align-right', 'align-center', 'image' , 'link', 'underline'].indexOf(e.btnid) < 0 && doc.queryCommandState(e.cmd));
33260                 e.setActive(doc.queryCommandState(e.cmd));
33261             }
33262         }, this);
33263         
33264         
33265         if (ev &&
33266             (ev.type == 'mouseup' || ev.type == 'click' ) &&
33267             ev.target && ev.target.tagName != 'BODY' ) { // && ev.target.tagName == 'IMG') {
33268             // they have click on an image...
33269             // let's see if we can change the selection...
33270             sel = ev.target;
33271             
33272         }
33273         
33274         var ans = this.editorcore.getAllAncestors();
33275         if (!sel) { 
33276             sel = ans.length ? (ans[0] ?  ans[0]  : ans[1]) : this.editorcore.doc.body;
33277             sel = sel ? sel : this.editorcore.doc.body;
33278             sel = sel.tagName.length ? sel : this.editorcore.doc.body;
33279             
33280         }
33281         
33282         var lastSel = this.selectedNode;
33283         this.selectedNode = sel;
33284          
33285         // ok see if we are editing a block?
33286         
33287         var db = false;
33288         // you are not actually selecting the block.
33289         if (sel && sel.hasAttribute('data-block')) {
33290             db = sel;
33291         } else if (sel && sel.closest('[data-block]')) {
33292             db = sel.closest('[data-block]');
33293         }
33294         
33295         Array.from(this.editorcore.doc.body.querySelectorAll('.roo-ed-selection')).forEach(function(e) {
33296             e.classList.remove('roo-ed-selection');
33297         });
33298         
33299         var block = false;
33300         if (db && this.editorcore.enableBlocks) {
33301             block = Roo.htmleditor.Block.factory(db);
33302             
33303             if (block) {
33304                 db.className =  (db.classList.length > 0  ? db.className + ' ' : '') +
33305                     ' roo-ed-selection';
33306                 sel = this.selectedNode = db;
33307             }
33308         }
33309         
33310         // highlight the 'a'..
33311         var tn = sel && sel.tagName.toUpperCase() || '';
33312         if (!block && sel && tn != 'A') {
33313             var asel = sel.closest('A');
33314             if (asel) {
33315                 sel = asel;
33316             }
33317         }
33318        
33319         btns.get('link').setActive(tn == 'A' && this.selectedNode.hasAttribute('href'));
33320         btns.get('image').setActive(tn == 'IMG' || this.editorcore.enableBlocks && tn == 'FIGURE');
33321         btns.get('underline').setActive(tn == 'U' || sel.closest('u') ? true : false);
33322         
33323         Roo.bootstrap.menu.Manager.hideAll();
33324          
33325         
33326         
33327         
33328         
33329         // handle delete button..
33330         if (hasToggle || (tn.length && tn == 'BODY')) {
33331             this.deleteBtn.hide();
33332             return;
33333             
33334         }
33335         this.deleteBtn.show();
33336         
33337         
33338         
33339         //this.editorsyncValue();
33340     },
33341     onFirstFocus: function() {
33342         this.buttons.each(function(item){
33343            item.enable();
33344         });
33345     },
33346     
33347     onDelete : function()
33348     {
33349         var range = this.editorcore.createRange();
33350         var selection = this.editorcore.getSelection();
33351         var sn = this.selectedNode;
33352         range.setStart(sn,0);
33353         range.setEnd(sn,0); 
33354         
33355         
33356         if (sn.hasAttribute('data-block')) {
33357             var block = Roo.htmleditor.Block.factory(this.selectedNode);
33358             if (block) {
33359                 sn = block.removeNode();
33360                 sn.parentNode.removeChild(sn);
33361                 selection.removeAllRanges();
33362                 selection.addRange(range);
33363                 this.updateToolbar(null, null, null);
33364                 if (sn.tagName.toUpperCase() == 'FIGURE') {
33365                     this.editor.syncValue();
33366                     this.editor.fireEvent('imagedelete', this.editor, sn);
33367                 }
33368                 
33369                 this.selectedNode = false;
33370                 this.editorcore.fireEditorEvent(false);
33371                 return;
33372             }   
33373              
33374         }
33375         if (!sn) {
33376             return; // should not really happen..
33377         }
33378         if (sn && sn.tagName == 'BODY') {
33379             return;
33380         }
33381         var stn =  sn.childNodes[0] || sn.nextSibling || sn.previousSibling || sn.parentNode;
33382         
33383         // remove and keep parents.
33384         a = new Roo.htmleditor.FilterKeepChildren({tag : false});
33385         a.replaceTag(sn);
33386         
33387         selection.removeAllRanges();
33388         selection.addRange(range);
33389         if (sn.tagName.toUpperCase() == 'IMG"') {
33390             this.editor.syncValue();
33391             this.editor.fireEvent('imagedelete', this.editor, sn);
33392         }
33393         
33394         this.selectedNode = false;
33395         this.editorcore.fireEditorEvent(false);
33396         
33397         
33398     },
33399     
33400     
33401     toggleSourceEdit : function(sourceEditMode){
33402         
33403           
33404         if(sourceEditMode){
33405             Roo.log("disabling buttons");
33406            this.buttons.each( function(item){
33407                 if(item.cmd != 'pencil'){
33408                     item.disable();
33409                 }
33410             });
33411           
33412         }else{
33413             Roo.log("enabling buttons");
33414             if(this.editorcore.initialized){
33415                 this.buttons.each( function(item){
33416                     item.enable();
33417                 });
33418             }
33419             
33420         }
33421         Roo.log("calling toggole on editor");
33422         // tell the editor that it's been pressed..
33423         this.editor.toggleSourceEdit(sourceEditMode);
33424        
33425     }
33426 });
33427
33428
33429
33430
33431  
33432 /*
33433  * - LGPL
33434  */
33435
33436 /**
33437  * @class Roo.bootstrap.form.Markdown
33438  * @extends Roo.bootstrap.form.TextArea
33439  * Bootstrap Showdown editable area
33440  * @cfg {string} content
33441  * 
33442  * @constructor
33443  * Create a new Showdown
33444  */
33445
33446 Roo.bootstrap.form.Markdown = function(config){
33447     Roo.bootstrap.form.Markdown.superclass.constructor.call(this, config);
33448    
33449 };
33450
33451 Roo.extend(Roo.bootstrap.form.Markdown, Roo.bootstrap.form.TextArea,  {
33452     
33453     editing :false,
33454     
33455     initEvents : function()
33456     {
33457         
33458         Roo.bootstrap.form.TextArea.prototype.initEvents.call(this);
33459         this.markdownEl = this.el.createChild({
33460             cls : 'roo-markdown-area'
33461         });
33462         this.inputEl().addClass('d-none');
33463         if (this.getValue() == '') {
33464             this.markdownEl.dom.innerHTML = String.format('<span class="roo-placeholder">{0}</span>', this.placeholder || '');
33465             
33466         } else {
33467             this.markdownEl.dom.innerHTML = Roo.Markdown.toHtml(Roo.util.Format.htmlEncode(this.getValue()));
33468         }
33469         this.markdownEl.on('click', this.toggleTextEdit, this);
33470         this.on('blur', this.toggleTextEdit, this);
33471         this.on('specialkey', this.resizeTextArea, this);
33472     },
33473     
33474     toggleTextEdit : function()
33475     {
33476         var sh = this.markdownEl.getHeight();
33477         this.inputEl().addClass('d-none');
33478         this.markdownEl.addClass('d-none');
33479         if (!this.editing) {
33480             // show editor?
33481             this.inputEl().setHeight(Math.min(500, Math.max(sh,(this.getValue().split("\n").length+1) * 30)));
33482             this.inputEl().removeClass('d-none');
33483             this.inputEl().focus();
33484             this.editing = true;
33485             return;
33486         }
33487         // show showdown...
33488         this.updateMarkdown();
33489         this.markdownEl.removeClass('d-none');
33490         this.editing = false;
33491         return;
33492     },
33493     updateMarkdown : function()
33494     {
33495         if (this.getValue() == '') {
33496             this.markdownEl.dom.innerHTML = String.format('<span class="roo-placeholder">{0}</span>', this.placeholder || '');
33497             return;
33498         }
33499  
33500         this.markdownEl.dom.innerHTML = Roo.Markdown.toHtml(Roo.util.Format.htmlEncode(this.getValue()));
33501     },
33502     
33503     resizeTextArea: function () {
33504         
33505         var sh = 100;
33506         Roo.log([sh, this.getValue().split("\n").length * 30]);
33507         this.inputEl().setHeight(Math.min(500, Math.max(sh, (this.getValue().split("\n").length +1) * 30)));
33508     },
33509     setValue : function(val)
33510     {
33511         Roo.bootstrap.form.TextArea.prototype.setValue.call(this,val);
33512         if (!this.editing) {
33513             this.updateMarkdown();
33514         }
33515         
33516     },
33517     focus : function()
33518     {
33519         if (!this.editing) {
33520             this.toggleTextEdit();
33521         }
33522         
33523     }
33524
33525
33526 });/*
33527  * Based on:
33528  * Ext JS Library 1.1.1
33529  * Copyright(c) 2006-2007, Ext JS, LLC.
33530  *
33531  * Originally Released Under LGPL - original licence link has changed is not relivant.
33532  *
33533  * Fork - LGPL
33534  * <script type="text/javascript">
33535  */
33536  
33537 /**
33538  * @class Roo.bootstrap.PagingToolbar
33539  * @extends Roo.bootstrap.nav.Simplebar
33540  * A specialized toolbar that is bound to a {@link Roo.data.Store} and provides automatic paging controls.
33541  * @constructor
33542  * Create a new PagingToolbar
33543  * @param {Object} config The config object
33544  * @param {Roo.data.Store} store
33545  */
33546 Roo.bootstrap.PagingToolbar = function(config)
33547 {
33548     // old args format still supported... - xtype is prefered..
33549         // created from xtype...
33550     
33551     this.ds = config.dataSource;
33552     
33553     if (config.store && !this.ds) {
33554         this.store= Roo.factory(config.store, Roo.data);
33555         this.ds = this.store;
33556         this.ds.xmodule = this.xmodule || false;
33557     }
33558     
33559     this.toolbarItems = [];
33560     if (config.items) {
33561         this.toolbarItems = config.items;
33562     }
33563     
33564     Roo.bootstrap.PagingToolbar.superclass.constructor.call(this, config);
33565     
33566     this.cursor = 0;
33567     
33568     if (this.ds) { 
33569         this.bind(this.ds);
33570     }
33571     
33572     if (Roo.bootstrap.version == 4) {
33573         this.navgroup = new Roo.bootstrap.ButtonGroup({ cls: 'pagination' });
33574     } else {
33575         this.navgroup = new Roo.bootstrap.nav.Group({ cls: 'pagination' });
33576     }
33577     
33578 };
33579
33580 Roo.extend(Roo.bootstrap.PagingToolbar, Roo.bootstrap.nav.Simplebar, {
33581     /**
33582      * @cfg {Roo.bootstrap.Button} buttons[]
33583      * Buttons for the toolbar
33584      */
33585      /**
33586      * @cfg {Roo.data.Store} store
33587      * The underlying data store providing the paged data
33588      */
33589     /**
33590      * @cfg {String/HTMLElement/Element} container
33591      * container The id or element that will contain the toolbar
33592      */
33593     /**
33594      * @cfg {Boolean} displayInfo
33595      * True to display the displayMsg (defaults to false)
33596      */
33597     /**
33598      * @cfg {Number} pageSize
33599      * The number of records to display per page (defaults to 20)
33600      */
33601     pageSize: 20,
33602     /**
33603      * @cfg {String} displayMsg
33604      * The paging status message to display (defaults to "Displaying {start} - {end} of {total}")
33605      */
33606     displayMsg : 'Displaying {0} - {1} of {2}',
33607     /**
33608      * @cfg {String} emptyMsg
33609      * The message to display when no records are found (defaults to "No data to display")
33610      */
33611     emptyMsg : 'No data to display',
33612     /**
33613      * Customizable piece of the default paging text (defaults to "Page")
33614      * @type String
33615      */
33616     beforePageText : "Page",
33617     /**
33618      * Customizable piece of the default paging text (defaults to "of %0")
33619      * @type String
33620      */
33621     afterPageText : "of {0}",
33622     /**
33623      * Customizable piece of the default paging text (defaults to "First Page")
33624      * @type String
33625      */
33626     firstText : "First Page",
33627     /**
33628      * Customizable piece of the default paging text (defaults to "Previous Page")
33629      * @type String
33630      */
33631     prevText : "Previous Page",
33632     /**
33633      * Customizable piece of the default paging text (defaults to "Next Page")
33634      * @type String
33635      */
33636     nextText : "Next Page",
33637     /**
33638      * Customizable piece of the default paging text (defaults to "Last Page")
33639      * @type String
33640      */
33641     lastText : "Last Page",
33642     /**
33643      * Customizable piece of the default paging text (defaults to "Refresh")
33644      * @type String
33645      */
33646     refreshText : "Refresh",
33647
33648     buttons : false,
33649     // private
33650     onRender : function(ct, position) 
33651     {
33652         Roo.bootstrap.PagingToolbar.superclass.onRender.call(this, ct, position);
33653         this.navgroup.parentId = this.id;
33654         this.navgroup.onRender(this.el, null);
33655         // add the buttons to the navgroup
33656         
33657         if(this.displayInfo){
33658             this.el.select('ul.navbar-nav',true).first().createChild({cls:'x-paging-info'});
33659             this.displayEl = this.el.select('.x-paging-info', true).first();
33660 //            var navel = this.navgroup.addItem( { tagtype : 'span', html : '', cls : 'x-paging-info', preventDefault : true } );
33661 //            this.displayEl = navel.el.select('span',true).first();
33662         }
33663         
33664         var _this = this;
33665         
33666         if(this.buttons){
33667             Roo.each(_this.buttons, function(e){ // this might need to use render????
33668                Roo.factory(e).render(_this.el);
33669             });
33670         }
33671             
33672         Roo.each(_this.toolbarItems, function(e) {
33673             _this.navgroup.addItem(e);
33674         });
33675         
33676         
33677         this.first = this.navgroup.addItem({
33678             tooltip: this.firstText,
33679             cls: "prev btn-outline-secondary",
33680             html : ' <i class="fa fa-step-backward"></i>',
33681             disabled: true,
33682             preventDefault: true,
33683             listeners : { click : this.onClick.createDelegate(this, ["first"]) }
33684         });
33685         
33686         this.prev =  this.navgroup.addItem({
33687             tooltip: this.prevText,
33688             cls: "prev btn-outline-secondary",
33689             html : ' <i class="fa fa-backward"></i>',
33690             disabled: true,
33691             preventDefault: true,
33692             listeners : { click :  this.onClick.createDelegate(this, ["prev"]) }
33693         });
33694     //this.addSeparator();
33695         
33696         
33697         var field = this.navgroup.addItem( {
33698             tagtype : 'span',
33699             cls : 'x-paging-position  btn-outline-secondary',
33700              disabled: true,
33701             html : this.beforePageText  +
33702                 '<input type="text" size="3" value="1" class="x-grid-page-number">' +
33703                 '<span class="x-paging-after">' +  String.format(this.afterPageText, 1) + '</span>'
33704          } ); //?? escaped?
33705         
33706         this.field = field.el.select('input', true).first();
33707         this.field.on("keydown", this.onPagingKeydown, this);
33708         this.field.on("focus", function(){this.dom.select();});
33709     
33710     
33711         this.afterTextEl =  field.el.select('.x-paging-after',true).first();
33712         //this.field.setHeight(18);
33713         //this.addSeparator();
33714         this.next = this.navgroup.addItem({
33715             tooltip: this.nextText,
33716             cls: "next btn-outline-secondary",
33717             html : ' <i class="fa fa-forward"></i>',
33718             disabled: true,
33719             preventDefault: true,
33720             listeners : { click :  this.onClick.createDelegate(this, ["next"]) }
33721         });
33722         this.last = this.navgroup.addItem({
33723             tooltip: this.lastText,
33724             html : ' <i class="fa fa-step-forward"></i>',
33725             cls: "next btn-outline-secondary",
33726             disabled: true,
33727             preventDefault: true,
33728             listeners : { click :  this.onClick.createDelegate(this, ["last"]) }
33729         });
33730     //this.addSeparator();
33731         this.loading = this.navgroup.addItem({
33732             tooltip: this.refreshText,
33733             cls: "btn-outline-secondary",
33734             html : ' <i class="fa fa-refresh"></i>',
33735             preventDefault: true,
33736             listeners : { click : this.onClick.createDelegate(this, ["refresh"]) }
33737         });
33738         
33739     },
33740
33741     // private
33742     updateInfo : function(){
33743         if(this.displayEl){
33744             var count = (typeof(this.getCount) == 'undefined') ? this.ds.getCount() : this.getCount();
33745             var msg = count == 0 ?
33746                 this.emptyMsg :
33747                 String.format(
33748                     this.displayMsg,
33749                     this.cursor+1, this.cursor+count, this.ds.getTotalCount()    
33750                 );
33751             this.displayEl.update(msg);
33752         }
33753     },
33754
33755     // private
33756     onLoad : function(ds, r, o)
33757     {
33758         this.cursor = o.params && o.params.start ? o.params.start : 0;
33759         
33760         var d = this.getPageData(),
33761             ap = d.activePage,
33762             ps = d.pages;
33763         
33764         
33765         this.afterTextEl.dom.innerHTML = String.format(this.afterPageText, d.pages);
33766         this.field.dom.value = ap;
33767         this.first.setDisabled(ap == 1);
33768         this.prev.setDisabled(ap == 1);
33769         this.next.setDisabled(ap == ps);
33770         this.last.setDisabled(ap == ps);
33771         this.loading.enable();
33772         this.updateInfo();
33773     },
33774
33775     // private
33776     getPageData : function(){
33777         var total = this.ds.getTotalCount();
33778         return {
33779             total : total,
33780             activePage : Math.ceil((this.cursor+this.pageSize)/this.pageSize),
33781             pages :  total < this.pageSize ? 1 : Math.ceil(total/this.pageSize)
33782         };
33783     },
33784
33785     // private
33786     onLoadError : function(proxy, o){
33787         this.loading.enable();
33788         if (this.ds.events.loadexception.listeners.length  < 2) {
33789             // nothing has been assigned to loadexception except this...
33790             // so 
33791             Roo.MessageBox.alert("Error loading",o.raw.errorMsg);
33792
33793         }
33794     },
33795
33796     // private
33797     onPagingKeydown : function(e){
33798         var k = e.getKey();
33799         var d = this.getPageData();
33800         if(k == e.RETURN){
33801             var v = this.field.dom.value, pageNum;
33802             if(!v || isNaN(pageNum = parseInt(v, 10))){
33803                 this.field.dom.value = d.activePage;
33804                 return;
33805             }
33806             pageNum = Math.min(Math.max(1, pageNum), d.pages) - 1;
33807             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
33808             e.stopEvent();
33809         }
33810         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))
33811         {
33812           var pageNum = (k == e.HOME || (k == e.DOWN && e.ctrlKey) || (k == e.LEFT && e.ctrlKey) || (k == e.PAGEDOWN && e.ctrlKey)) ? 1 : d.pages;
33813           this.field.dom.value = pageNum;
33814           this.ds.load({params:{start: (pageNum - 1) * this.pageSize, limit: this.pageSize}});
33815           e.stopEvent();
33816         }
33817         else if(k == e.UP || k == e.RIGHT || k == e.PAGEUP || k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
33818         {
33819           var v = this.field.dom.value, pageNum; 
33820           var increment = (e.shiftKey) ? 10 : 1;
33821           if(k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN) {
33822                 increment *= -1;
33823           }
33824           if(!v || isNaN(pageNum = parseInt(v, 10))) {
33825             this.field.dom.value = d.activePage;
33826             return;
33827           }
33828           else if(parseInt(v, 10) + increment >= 1 & parseInt(v, 10) + increment <= d.pages)
33829           {
33830             this.field.dom.value = parseInt(v, 10) + increment;
33831             pageNum = Math.min(Math.max(1, pageNum + increment), d.pages) - 1;
33832             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
33833           }
33834           e.stopEvent();
33835         }
33836     },
33837
33838     // private
33839     beforeLoad : function(){
33840         if(this.loading){
33841             this.loading.disable();
33842         }
33843     },
33844
33845     // private
33846     onClick : function(which){
33847         
33848         var ds = this.ds;
33849         if (!ds) {
33850             return;
33851         }
33852         
33853         switch(which){
33854             case "first":
33855                 ds.load({params:{start: 0, limit: this.pageSize}});
33856             break;
33857             case "prev":
33858                 ds.load({params:{start: Math.max(0, this.cursor-this.pageSize), limit: this.pageSize}});
33859             break;
33860             case "next":
33861                 ds.load({params:{start: this.cursor+this.pageSize, limit: this.pageSize}});
33862             break;
33863             case "last":
33864                 var total = ds.getTotalCount();
33865                 var extra = total % this.pageSize;
33866                 var lastStart = extra ? (total - extra) : total-this.pageSize;
33867                 ds.load({params:{start: lastStart, limit: this.pageSize}});
33868             break;
33869             case "refresh":
33870                 ds.load({params:{start: this.cursor, limit: this.pageSize}});
33871             break;
33872         }
33873     },
33874
33875     /**
33876      * Unbinds the paging toolbar from the specified {@link Roo.data.Store}
33877      * @param {Roo.data.Store} store The data store to unbind
33878      */
33879     unbind : function(ds){
33880         ds.un("beforeload", this.beforeLoad, this);
33881         ds.un("load", this.onLoad, this);
33882         ds.un("loadexception", this.onLoadError, this);
33883         ds.un("remove", this.updateInfo, this);
33884         ds.un("add", this.updateInfo, this);
33885         this.ds = undefined;
33886     },
33887
33888     /**
33889      * Binds the paging toolbar to the specified {@link Roo.data.Store}
33890      * @param {Roo.data.Store} store The data store to bind
33891      */
33892     bind : function(ds){
33893         ds.on("beforeload", this.beforeLoad, this);
33894         ds.on("load", this.onLoad, this);
33895         ds.on("loadexception", this.onLoadError, this);
33896         ds.on("remove", this.updateInfo, this);
33897         ds.on("add", this.updateInfo, this);
33898         this.ds = ds;
33899     }
33900 });/*
33901  * - LGPL
33902  *
33903  * element
33904  * 
33905  */
33906
33907 /**
33908  * @class Roo.bootstrap.MessageBar
33909  * @extends Roo.bootstrap.Component
33910  * Bootstrap MessageBar class
33911  * @cfg {String} html contents of the MessageBar
33912  * @cfg {String} weight (info | success | warning | danger) default info
33913  * @cfg {String} beforeClass insert the bar before the given class
33914  * @cfg {Boolean} closable (true | false) default false
33915  * @cfg {Boolean} fixed (true | false) default false, fix the bar at the top
33916  * 
33917  * @constructor
33918  * Create a new Element
33919  * @param {Object} config The config object
33920  */
33921
33922 Roo.bootstrap.MessageBar = function(config){
33923     Roo.bootstrap.MessageBar.superclass.constructor.call(this, config);
33924 };
33925
33926 Roo.extend(Roo.bootstrap.MessageBar, Roo.bootstrap.Component,  {
33927     
33928     html: '',
33929     weight: 'info',
33930     closable: false,
33931     fixed: false,
33932     beforeClass: 'bootstrap-sticky-wrap',
33933     
33934     getAutoCreate : function(){
33935         
33936         var cfg = {
33937             tag: 'div',
33938             cls: 'alert alert-dismissable alert-' + this.weight,
33939             cn: [
33940                 {
33941                     tag: 'span',
33942                     cls: 'message',
33943                     html: this.html || ''
33944                 }
33945             ]
33946         };
33947         
33948         if(this.fixed){
33949             cfg.cls += ' alert-messages-fixed';
33950         }
33951         
33952         if(this.closable){
33953             cfg.cn.push({
33954                 tag: 'button',
33955                 cls: 'close',
33956                 html: 'x'
33957             });
33958         }
33959         
33960         return cfg;
33961     },
33962     
33963     onRender : function(ct, position)
33964     {
33965         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
33966         
33967         if(!this.el){
33968             var cfg = Roo.apply({},  this.getAutoCreate());
33969             cfg.id = Roo.id();
33970             
33971             if (this.cls) {
33972                 cfg.cls += ' ' + this.cls;
33973             }
33974             if (this.style) {
33975                 cfg.style = this.style;
33976             }
33977             this.el = Roo.get(document.body).createChild(cfg, Roo.select('.'+this.beforeClass, true).first());
33978             
33979             this.el.setVisibilityMode(Roo.Element.DISPLAY);
33980         }
33981         
33982         this.el.select('>button.close').on('click', this.hide, this);
33983         
33984     },
33985     
33986     show : function()
33987     {
33988         if (!this.rendered) {
33989             this.render();
33990         }
33991         
33992         this.el.show();
33993         
33994         this.fireEvent('show', this);
33995         
33996     },
33997     
33998     hide : function()
33999     {
34000         if (!this.rendered) {
34001             this.render();
34002         }
34003         
34004         this.el.hide();
34005         
34006         this.fireEvent('hide', this);
34007     },
34008     
34009     update : function()
34010     {
34011 //        var e = this.el.dom.firstChild;
34012 //        
34013 //        if(this.closable){
34014 //            e = e.nextSibling;
34015 //        }
34016 //        
34017 //        e.data = this.html || '';
34018
34019         this.el.select('>.message', true).first().dom.innerHTML = this.html || '';
34020     }
34021    
34022 });
34023
34024  
34025
34026      /*
34027  * - LGPL
34028  *
34029  * Graph
34030  * 
34031  */
34032
34033
34034 /**
34035  * @class Roo.bootstrap.Graph
34036  * @extends Roo.bootstrap.Component
34037  * Bootstrap Graph class
34038 > Prameters
34039  -sm {number} sm 4
34040  -md {number} md 5
34041  @cfg {String} graphtype  bar | vbar | pie
34042  @cfg {number} g_x coodinator | centre x (pie)
34043  @cfg {number} g_y coodinator | centre y (pie)
34044  @cfg {number} g_r radius (pie)
34045  @cfg {number} g_height height of the chart (respected by all elements in the set)
34046  @cfg {number} g_width width of the chart (respected by all elements in the set)
34047  @cfg {Object} title The title of the chart
34048     
34049  -{Array}  values
34050  -opts (object) options for the chart 
34051      o {
34052      o type (string) type of endings of the bar. Default: 'square'. Other options are: 'round', 'sharp', 'soft'.
34053      o gutter (number)(string) default '20%' (WHAT DOES IT DO?)
34054      o vgutter (number)
34055      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.
34056      o stacked (boolean) whether or not to tread values as in a stacked bar chart
34057      o to
34058      o stretch (boolean)
34059      o }
34060  -opts (object) options for the pie
34061      o{
34062      o cut
34063      o startAngle (number)
34064      o endAngle (number)
34065      } 
34066  *
34067  * @constructor
34068  * Create a new Input
34069  * @param {Object} config The config object
34070  */
34071
34072 Roo.bootstrap.Graph = function(config){
34073     Roo.bootstrap.Graph.superclass.constructor.call(this, config);
34074     
34075     this.addEvents({
34076         // img events
34077         /**
34078          * @event click
34079          * The img click event for the img.
34080          * @param {Roo.EventObject} e
34081          */
34082         "click" : true
34083     });
34084 };
34085
34086 Roo.extend(Roo.bootstrap.Graph, Roo.bootstrap.Component,  {
34087     
34088     sm: 4,
34089     md: 5,
34090     graphtype: 'bar',
34091     g_height: 250,
34092     g_width: 400,
34093     g_x: 50,
34094     g_y: 50,
34095     g_r: 30,
34096     opts:{
34097         //g_colors: this.colors,
34098         g_type: 'soft',
34099         g_gutter: '20%'
34100
34101     },
34102     title : false,
34103
34104     getAutoCreate : function(){
34105         
34106         var cfg = {
34107             tag: 'div',
34108             html : null
34109         };
34110         
34111         
34112         return  cfg;
34113     },
34114
34115     onRender : function(ct,position){
34116         
34117         
34118         Roo.bootstrap.Graph.superclass.onRender.call(this,ct,position);
34119         
34120         if (typeof(Raphael) == 'undefined') {
34121             Roo.bootstrap.MessageBox.alert("Error","Raphael is not availabe");
34122             return;
34123         }
34124         
34125         this.raphael = Raphael(this.el.dom);
34126         
34127                     // data1 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
34128                     // data2 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
34129                     // data3 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
34130                     // txtattr = { font: "12px 'Fontin Sans', Fontin-Sans, sans-serif" };
34131                 /*
34132                 r.text(160, 10, "Single Series Chart").attr(txtattr);
34133                 r.text(480, 10, "Multiline Series Chart").attr(txtattr);
34134                 r.text(160, 250, "Multiple Series Stacked Chart").attr(txtattr);
34135                 r.text(480, 250, 'Multiline Series Stacked Vertical Chart. Type "round"').attr(txtattr);
34136                 
34137                 r.barchart(10, 10, 300, 220, [[55, 20, 13, 32, 5, 1, 2, 10]], 0, {type: "sharp"});
34138                 r.barchart(330, 10, 300, 220, data1);
34139                 r.barchart(10, 250, 300, 220, data2, {stacked: true});
34140                 r.barchart(330, 250, 300, 220, data3, {stacked: true, type: "round"});
34141                 */
34142                 
34143                 // var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
34144                 // r.barchart(30, 30, 560, 250,  xdata, {
34145                 //    labels : [55, 20, 13, 32, 5, 1, 2, 10,5 , 10],
34146                 //     axis : "0 0 1 1",
34147                 //     axisxlabels :  xdata
34148                 //     //yvalues : cols,
34149                    
34150                 // });
34151 //        var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
34152 //        
34153 //        this.load(null,xdata,{
34154 //                axis : "0 0 1 1",
34155 //                axisxlabels :  xdata
34156 //                });
34157
34158     },
34159
34160     load : function(graphtype,xdata,opts)
34161     {
34162         this.raphael.clear();
34163         if(!graphtype) {
34164             graphtype = this.graphtype;
34165         }
34166         if(!opts){
34167             opts = this.opts;
34168         }
34169         var r = this.raphael,
34170             fin = function () {
34171                 this.flag = r.popup(this.bar.x, this.bar.y, this.bar.value || "0").insertBefore(this);
34172             },
34173             fout = function () {
34174                 this.flag.animate({opacity: 0}, 300, function () {this.remove();});
34175             },
34176             pfin = function() {
34177                 this.sector.stop();
34178                 this.sector.scale(1.1, 1.1, this.cx, this.cy);
34179
34180                 if (this.label) {
34181                     this.label[0].stop();
34182                     this.label[0].attr({ r: 7.5 });
34183                     this.label[1].attr({ "font-weight": 800 });
34184                 }
34185             },
34186             pfout = function() {
34187                 this.sector.animate({ transform: 's1 1 ' + this.cx + ' ' + this.cy }, 500, "bounce");
34188
34189                 if (this.label) {
34190                     this.label[0].animate({ r: 5 }, 500, "bounce");
34191                     this.label[1].attr({ "font-weight": 400 });
34192                 }
34193             };
34194
34195         switch(graphtype){
34196             case 'bar':
34197                 this.raphael.barchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
34198                 break;
34199             case 'hbar':
34200                 this.raphael.hbarchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
34201                 break;
34202             case 'pie':
34203 //                opts = { legend: ["%% - Enterprise Users", "% - ddd","Chrome Users"], legendpos: "west", 
34204 //                href: ["http://raphaeljs.com", "http://g.raphaeljs.com"]};
34205 //            
34206                 this.raphael.piechart(this.g_x,this.g_y,this.g_r,xdata,opts).hover(pfin, pfout);
34207                 
34208                 break;
34209
34210         }
34211         
34212         if(this.title){
34213             this.raphael.text(this.title.x, this.title.y, this.title.text).attr(this.title.attr);
34214         }
34215         
34216     },
34217     
34218     setTitle: function(o)
34219     {
34220         this.title = o;
34221     },
34222     
34223     initEvents: function() {
34224         
34225         if(!this.href){
34226             this.el.on('click', this.onClick, this);
34227         }
34228     },
34229     
34230     onClick : function(e)
34231     {
34232         Roo.log('img onclick');
34233         this.fireEvent('click', this, e);
34234     }
34235    
34236 });
34237
34238  
34239 Roo.bootstrap.dash = {};/*
34240  * - LGPL
34241  *
34242  * numberBox
34243  * 
34244  */
34245 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
34246
34247 /**
34248  * @class Roo.bootstrap.dash.NumberBox
34249  * @extends Roo.bootstrap.Component
34250  * Bootstrap NumberBox class
34251  * @cfg {String} headline Box headline
34252  * @cfg {String} content Box content
34253  * @cfg {String} icon Box icon
34254  * @cfg {String} footer Footer text
34255  * @cfg {String} fhref Footer href
34256  * 
34257  * @constructor
34258  * Create a new NumberBox
34259  * @param {Object} config The config object
34260  */
34261
34262
34263 Roo.bootstrap.dash.NumberBox = function(config){
34264     Roo.bootstrap.dash.NumberBox.superclass.constructor.call(this, config);
34265     
34266 };
34267
34268 Roo.extend(Roo.bootstrap.dash.NumberBox, Roo.bootstrap.Component,  {
34269     
34270     headline : '',
34271     content : '',
34272     icon : '',
34273     footer : '',
34274     fhref : '',
34275     ficon : '',
34276     
34277     getAutoCreate : function(){
34278         
34279         var cfg = {
34280             tag : 'div',
34281             cls : 'small-box ',
34282             cn : [
34283                 {
34284                     tag : 'div',
34285                     cls : 'inner',
34286                     cn :[
34287                         {
34288                             tag : 'h3',
34289                             cls : 'roo-headline',
34290                             html : this.headline
34291                         },
34292                         {
34293                             tag : 'p',
34294                             cls : 'roo-content',
34295                             html : this.content
34296                         }
34297                     ]
34298                 }
34299             ]
34300         };
34301         
34302         if(this.icon){
34303             cfg.cn.push({
34304                 tag : 'div',
34305                 cls : 'icon',
34306                 cn :[
34307                     {
34308                         tag : 'i',
34309                         cls : 'ion ' + this.icon
34310                     }
34311                 ]
34312             });
34313         }
34314         
34315         if(this.footer){
34316             var footer = {
34317                 tag : 'a',
34318                 cls : 'small-box-footer',
34319                 href : this.fhref || '#',
34320                 html : this.footer
34321             };
34322             
34323             cfg.cn.push(footer);
34324             
34325         }
34326         
34327         return  cfg;
34328     },
34329
34330     onRender : function(ct,position){
34331         Roo.bootstrap.dash.NumberBox.superclass.onRender.call(this,ct,position);
34332
34333
34334        
34335                 
34336     },
34337
34338     setHeadline: function (value)
34339     {
34340         this.el.select('.roo-headline',true).first().dom.innerHTML = value;
34341     },
34342     
34343     setFooter: function (value, href)
34344     {
34345         this.el.select('a.small-box-footer',true).first().dom.innerHTML = value;
34346         
34347         if(href){
34348             this.el.select('a.small-box-footer',true).first().attr('href', href);
34349         }
34350         
34351     },
34352
34353     setContent: function (value)
34354     {
34355         this.el.select('.roo-content',true).first().dom.innerHTML = value;
34356     },
34357
34358     initEvents: function() 
34359     {   
34360         
34361     }
34362     
34363 });
34364
34365  
34366 /*
34367  * - LGPL
34368  *
34369  * TabBox
34370  * 
34371  */
34372 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
34373
34374 /**
34375  * @class Roo.bootstrap.dash.TabBox
34376  * @extends Roo.bootstrap.Component
34377  * @children Roo.bootstrap.dash.TabPane
34378  * Bootstrap TabBox class
34379  * @cfg {String} title Title of the TabBox
34380  * @cfg {String} icon Icon of the TabBox
34381  * @cfg {Boolean} showtabs (true|false) show the tabs default true
34382  * @cfg {Boolean} tabScrollable (true|false) tab scrollable when mobile view default false
34383  * 
34384  * @constructor
34385  * Create a new TabBox
34386  * @param {Object} config The config object
34387  */
34388
34389
34390 Roo.bootstrap.dash.TabBox = function(config){
34391     Roo.bootstrap.dash.TabBox.superclass.constructor.call(this, config);
34392     this.addEvents({
34393         // raw events
34394         /**
34395          * @event addpane
34396          * When a pane is added
34397          * @param {Roo.bootstrap.dash.TabPane} pane
34398          */
34399         "addpane" : true,
34400         /**
34401          * @event activatepane
34402          * When a pane is activated
34403          * @param {Roo.bootstrap.dash.TabPane} pane
34404          */
34405         "activatepane" : true
34406         
34407          
34408     });
34409     
34410     this.panes = [];
34411 };
34412
34413 Roo.extend(Roo.bootstrap.dash.TabBox, Roo.bootstrap.Component,  {
34414
34415     title : '',
34416     icon : false,
34417     showtabs : true,
34418     tabScrollable : false,
34419     
34420     getChildContainer : function()
34421     {
34422         return this.el.select('.tab-content', true).first();
34423     },
34424     
34425     getAutoCreate : function(){
34426         
34427         var header = {
34428             tag: 'li',
34429             cls: 'pull-left header',
34430             html: this.title,
34431             cn : []
34432         };
34433         
34434         if(this.icon){
34435             header.cn.push({
34436                 tag: 'i',
34437                 cls: 'fa ' + this.icon
34438             });
34439         }
34440         
34441         var h = {
34442             tag: 'ul',
34443             cls: 'nav nav-tabs pull-right',
34444             cn: [
34445                 header
34446             ]
34447         };
34448         
34449         if(this.tabScrollable){
34450             h = {
34451                 tag: 'div',
34452                 cls: 'tab-header',
34453                 cn: [
34454                     {
34455                         tag: 'ul',
34456                         cls: 'nav nav-tabs pull-right',
34457                         cn: [
34458                             header
34459                         ]
34460                     }
34461                 ]
34462             };
34463         }
34464         
34465         var cfg = {
34466             tag: 'div',
34467             cls: 'nav-tabs-custom',
34468             cn: [
34469                 h,
34470                 {
34471                     tag: 'div',
34472                     cls: 'tab-content no-padding',
34473                     cn: []
34474                 }
34475             ]
34476         };
34477
34478         return  cfg;
34479     },
34480     initEvents : function()
34481     {
34482         //Roo.log('add add pane handler');
34483         this.on('addpane', this.onAddPane, this);
34484     },
34485      /**
34486      * Updates the box title
34487      * @param {String} html to set the title to.
34488      */
34489     setTitle : function(value)
34490     {
34491         this.el.select('.nav-tabs .header', true).first().dom.innerHTML = value;
34492     },
34493     onAddPane : function(pane)
34494     {
34495         this.panes.push(pane);
34496         //Roo.log('addpane');
34497         //Roo.log(pane);
34498         // tabs are rendere left to right..
34499         if(!this.showtabs){
34500             return;
34501         }
34502         
34503         var ctr = this.el.select('.nav-tabs', true).first();
34504          
34505          
34506         var existing = ctr.select('.nav-tab',true);
34507         var qty = existing.getCount();;
34508         
34509         
34510         var tab = ctr.createChild({
34511             tag : 'li',
34512             cls : 'nav-tab' + (qty ? '' : ' active'),
34513             cn : [
34514                 {
34515                     tag : 'a',
34516                     href:'#',
34517                     html : pane.title
34518                 }
34519             ]
34520         }, qty ? existing.first().dom : ctr.select('.header', true).first().dom );
34521         pane.tab = tab;
34522         
34523         tab.on('click', this.onTabClick.createDelegate(this, [pane], true));
34524         if (!qty) {
34525             pane.el.addClass('active');
34526         }
34527         
34528                 
34529     },
34530     onTabClick : function(ev,un,ob,pane)
34531     {
34532         //Roo.log('tab - prev default');
34533         ev.preventDefault();
34534         
34535         
34536         this.el.select('.nav-tabs li.nav-tab', true).removeClass('active');
34537         pane.tab.addClass('active');
34538         //Roo.log(pane.title);
34539         this.getChildContainer().select('.tab-pane',true).removeClass('active');
34540         // technically we should have a deactivate event.. but maybe add later.
34541         // and it should not de-activate the selected tab...
34542         this.fireEvent('activatepane', pane);
34543         pane.el.addClass('active');
34544         pane.fireEvent('activate');
34545         
34546         
34547     },
34548     
34549     getActivePane : function()
34550     {
34551         var r = false;
34552         Roo.each(this.panes, function(p) {
34553             if(p.el.hasClass('active')){
34554                 r = p;
34555                 return false;
34556             }
34557             
34558             return;
34559         });
34560         
34561         return r;
34562     }
34563     
34564     
34565 });
34566
34567  
34568 /*
34569  * - LGPL
34570  *
34571  * Tab pane
34572  * 
34573  */
34574 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
34575 /**
34576  * @class Roo.bootstrap.TabPane
34577  * @extends Roo.bootstrap.Component
34578  * @children  Roo.bootstrap.Graph Roo.bootstrap.Column
34579  * Bootstrap TabPane class
34580  * @cfg {Boolean} active (false | true) Default false
34581  * @cfg {String} title title of panel
34582
34583  * 
34584  * @constructor
34585  * Create a new TabPane
34586  * @param {Object} config The config object
34587  */
34588
34589 Roo.bootstrap.dash.TabPane = function(config){
34590     Roo.bootstrap.dash.TabPane.superclass.constructor.call(this, config);
34591     
34592     this.addEvents({
34593         // raw events
34594         /**
34595          * @event activate
34596          * When a pane is activated
34597          * @param {Roo.bootstrap.dash.TabPane} pane
34598          */
34599         "activate" : true
34600          
34601     });
34602 };
34603
34604 Roo.extend(Roo.bootstrap.dash.TabPane, Roo.bootstrap.Component,  {
34605     
34606     active : false,
34607     title : '',
34608     
34609     // the tabBox that this is attached to.
34610     tab : false,
34611      
34612     getAutoCreate : function() 
34613     {
34614         var cfg = {
34615             tag: 'div',
34616             cls: 'tab-pane'
34617         };
34618         
34619         if(this.active){
34620             cfg.cls += ' active';
34621         }
34622         
34623         return cfg;
34624     },
34625     initEvents  : function()
34626     {
34627         //Roo.log('trigger add pane handler');
34628         this.parent().fireEvent('addpane', this)
34629     },
34630     
34631      /**
34632      * Updates the tab title 
34633      * @param {String} html to set the title to.
34634      */
34635     setTitle: function(str)
34636     {
34637         if (!this.tab) {
34638             return;
34639         }
34640         this.title = str;
34641         this.tab.select('a', true).first().dom.innerHTML = str;
34642         
34643     }
34644     
34645     
34646     
34647 });
34648
34649  
34650
34651
34652  /*
34653  * - LGPL
34654  *
34655  * Tooltip
34656  * 
34657  */
34658
34659 /**
34660  * @class Roo.bootstrap.Tooltip
34661  * Bootstrap Tooltip class
34662  * This is basic at present - all componets support it by default, however they should add tooltipEl() method
34663  * to determine which dom element triggers the tooltip.
34664  * 
34665  * It needs to add support for additional attributes like tooltip-position
34666  * 
34667  * @constructor
34668  * Create a new Toolti
34669  * @param {Object} config The config object
34670  */
34671
34672 Roo.bootstrap.Tooltip = function(config){
34673     Roo.bootstrap.Tooltip.superclass.constructor.call(this, config);
34674     
34675     this.alignment = Roo.bootstrap.Tooltip.alignment;
34676     
34677     if(typeof(config) != 'undefined' && typeof(config.alignment) != 'undefined'){
34678         this.alignment = config.alignment;
34679     }
34680     
34681 };
34682
34683 Roo.apply(Roo.bootstrap.Tooltip, {
34684     /**
34685      * @function init initialize tooltip monitoring.
34686      * @static
34687      */
34688     currentEl : false,
34689     currentTip : false,
34690     currentRegion : false,
34691     
34692     //  init : delay?
34693     
34694     init : function()
34695     {
34696         Roo.get(document).on('mouseover', this.enter ,this);
34697         Roo.get(document).on('mouseout', this.leave, this);
34698          
34699         
34700         this.currentTip = new Roo.bootstrap.Tooltip();
34701     },
34702     
34703     enter : function(ev)
34704     {
34705         var dom = ev.getTarget();
34706         
34707         //Roo.log(['enter',dom]);
34708         var el = Roo.fly(dom);
34709         if (this.currentEl) {
34710             //Roo.log(dom);
34711             //Roo.log(this.currentEl);
34712             //Roo.log(this.currentEl.contains(dom));
34713             if (this.currentEl == el) {
34714                 return;
34715             }
34716             if (dom != this.currentEl.dom && this.currentEl.contains(dom)) {
34717                 return;
34718             }
34719
34720         }
34721         
34722         if (this.currentTip.el) {
34723             this.currentTip.el.setVisibilityMode(Roo.Element.DISPLAY).hide(); // force hiding...
34724         }    
34725         //Roo.log(ev);
34726         
34727         if(!el || el.dom == document){
34728             return;
34729         }
34730         
34731         var bindEl = el; 
34732         var pel = false;
34733         if (!el.attr('tooltip')) {
34734             pel = el.findParent("[tooltip]");
34735             if (pel) {
34736                 bindEl = Roo.get(pel);
34737             }
34738         }
34739         
34740        
34741         
34742         // you can not look for children, as if el is the body.. then everythign is the child..
34743         if (!pel && !el.attr('tooltip')) { //
34744             if (!el.select("[tooltip]").elements.length) {
34745                 return;
34746             }
34747             // is the mouse over this child...?
34748             bindEl = el.select("[tooltip]").first();
34749             var xy = ev.getXY();
34750             if (!bindEl.getRegion().contains( { top : xy[1] ,right : xy[0] , bottom : xy[1], left : xy[0]})) {
34751                 //Roo.log("not in region.");
34752                 return;
34753             }
34754             //Roo.log("child element over..");
34755             
34756         }
34757         this.currentEl = el;
34758         this.currentTip.bind(bindEl);
34759         this.currentRegion = Roo.lib.Region.getRegion(dom);
34760         this.currentTip.enter();
34761         
34762     },
34763     leave : function(ev)
34764     {
34765         var dom = ev.getTarget();
34766         //Roo.log(['leave',dom]);
34767         if (!this.currentEl) {
34768             return;
34769         }
34770         
34771         
34772         if (dom != this.currentEl.dom) {
34773             return;
34774         }
34775         var xy = ev.getXY();
34776         if (this.currentRegion.contains( new Roo.lib.Region( xy[1], xy[0] ,xy[1], xy[0]  ))) {
34777             return;
34778         }
34779         // only activate leave if mouse cursor is outside... bounding box..
34780         
34781         
34782         
34783         
34784         if (this.currentTip) {
34785             this.currentTip.leave();
34786         }
34787         //Roo.log('clear currentEl');
34788         this.currentEl = false;
34789         
34790         
34791     },
34792     alignment : {
34793         'left' : ['r-l', [-2,0], 'right'],
34794         'right' : ['l-r', [2,0], 'left'],
34795         'bottom' : ['t-b', [0,2], 'top'],
34796         'top' : [ 'b-t', [0,-2], 'bottom']
34797     }
34798     
34799 });
34800
34801
34802 Roo.extend(Roo.bootstrap.Tooltip, Roo.bootstrap.Component,  {
34803     
34804     
34805     bindEl : false,
34806     
34807     delay : null, // can be { show : 300 , hide: 500}
34808     
34809     timeout : null,
34810     
34811     hoverState : null, //???
34812     
34813     placement : 'bottom', 
34814     
34815     alignment : false,
34816     
34817     getAutoCreate : function(){
34818     
34819         var cfg = {
34820            cls : 'tooltip',   
34821            role : 'tooltip',
34822            cn : [
34823                 {
34824                     cls : 'tooltip-arrow arrow'
34825                 },
34826                 {
34827                     cls : 'tooltip-inner'
34828                 }
34829            ]
34830         };
34831         
34832         return cfg;
34833     },
34834     bind : function(el)
34835     {
34836         this.bindEl = el;
34837     },
34838     
34839     initEvents : function()
34840     {
34841         this.arrowEl = this.el.select('.arrow', true).first();
34842         this.innerEl = this.el.select('.tooltip-inner', true).first();
34843     },
34844     
34845     enter : function () {
34846        
34847         if (this.timeout != null) {
34848             clearTimeout(this.timeout);
34849         }
34850         
34851         this.hoverState = 'in';
34852          //Roo.log("enter - show");
34853         if (!this.delay || !this.delay.show) {
34854             this.show();
34855             return;
34856         }
34857         var _t = this;
34858         this.timeout = setTimeout(function () {
34859             if (_t.hoverState == 'in') {
34860                 _t.show();
34861             }
34862         }, this.delay.show);
34863     },
34864     leave : function()
34865     {
34866         clearTimeout(this.timeout);
34867     
34868         this.hoverState = 'out';
34869          if (!this.delay || !this.delay.hide) {
34870             this.hide();
34871             return;
34872         }
34873        
34874         var _t = this;
34875         this.timeout = setTimeout(function () {
34876             //Roo.log("leave - timeout");
34877             
34878             if (_t.hoverState == 'out') {
34879                 _t.hide();
34880                 Roo.bootstrap.Tooltip.currentEl = false;
34881             }
34882         }, delay);
34883     },
34884     
34885     show : function (msg)
34886     {
34887         if (!this.el) {
34888             this.render(document.body);
34889         }
34890         // set content.
34891         //Roo.log([this.bindEl, this.bindEl.attr('tooltip')]);
34892         
34893         var tip = msg || this.bindEl.attr('tooltip') || this.bindEl.select("[tooltip]").first().attr('tooltip');
34894         
34895         this.el.select('.tooltip-inner',true).first().dom.innerHTML = tip;
34896         
34897         this.el.removeClass(['fade','top','bottom', 'left', 'right','in',
34898                              'bs-tooltip-top','bs-tooltip-bottom', 'bs-tooltip-left', 'bs-tooltip-right']);
34899
34900         if(this.bindEl.attr('tooltip-class')) {
34901             this.el.addClass(this.bindEl.attr('tooltip-class'));
34902         }
34903         
34904         var placement = typeof this.placement == 'function' ?
34905             this.placement.call(this, this.el, on_el) :
34906             this.placement;
34907         
34908         if(this.bindEl.attr('tooltip-placement')) {
34909             placement = this.bindEl.attr('tooltip-placement');
34910         }
34911             
34912         var autoToken = /\s?auto?\s?/i;
34913         var autoPlace = autoToken.test(placement);
34914         if (autoPlace) {
34915             placement = placement.replace(autoToken, '') || 'top';
34916         }
34917         
34918         //this.el.detach()
34919         //this.el.setXY([0,0]);
34920         this.el.show();
34921         //this.el.dom.style.display='block';
34922         
34923         //this.el.appendTo(on_el);
34924         
34925         var p = this.getPosition();
34926         var box = this.el.getBox();
34927         
34928         if (autoPlace) {
34929             // fixme..
34930         }
34931         
34932         var align = this.alignment[placement];
34933         
34934         var xy = this.el.getAlignToXY(this.bindEl, align[0], align[1]);
34935         
34936         if(placement == 'top' || placement == 'bottom'){
34937             if(xy[0] < 0){
34938                 placement = 'right';
34939             }
34940             
34941             if(xy[0] + this.el.getWidth() > Roo.lib.Dom.getViewWidth()){
34942                 placement = 'left';
34943             }
34944             
34945             var scroll = Roo.select('body', true).first().getScroll();
34946             
34947             if(xy[1] > Roo.lib.Dom.getViewHeight() + scroll.top - this.el.getHeight()){
34948                 placement = 'top';
34949             }
34950             
34951             align = this.alignment[placement];
34952             
34953             this.arrowEl.setLeft((this.innerEl.getWidth()/2) - 5);
34954             
34955         }
34956         
34957         var elems = document.getElementsByTagName('div');
34958         var highest = Number.MIN_SAFE_INTEGER || -(Math.pow(2, 53) - 1);
34959         for (var i = 0; i < elems.length; i++) {
34960           var zindex = Number.parseInt(
34961                 document.defaultView.getComputedStyle(elems[i], null).getPropertyValue("z-index"),
34962                 10
34963           );
34964           if (zindex > highest) {
34965             highest = zindex;
34966           }
34967         }
34968         
34969         
34970         
34971         this.el.dom.style.zIndex = highest;
34972         
34973         this.el.alignTo(this.bindEl, align[0],align[1]);
34974         //var arrow = this.el.select('.arrow',true).first();
34975         //arrow.set(align[2], 
34976         
34977         this.el.addClass(placement);
34978         this.el.addClass("bs-tooltip-"+ placement);
34979         
34980         this.el.addClass('in fade show');
34981         
34982         this.hoverState = null;
34983         
34984         if (this.el.hasClass('fade')) {
34985             // fade it?
34986         }
34987         
34988         
34989         
34990         
34991         
34992     },
34993     hide : function()
34994     {
34995          
34996         if (!this.el) {
34997             return;
34998         }
34999         //this.el.setXY([0,0]);
35000         if(this.bindEl.attr('tooltip-class')) {
35001             this.el.removeClass(this.bindEl.attr('tooltip-class'));
35002         }
35003         this.el.removeClass(['show', 'in']);
35004         //this.el.hide();
35005         
35006     }
35007     
35008 });
35009  
35010
35011  /*
35012  * - LGPL
35013  *
35014  * Location Picker
35015  * 
35016  */
35017
35018 /**
35019  * @class Roo.bootstrap.LocationPicker
35020  * @extends Roo.bootstrap.Component
35021  * Bootstrap LocationPicker class
35022  * @cfg {Number} latitude Position when init default 0
35023  * @cfg {Number} longitude Position when init default 0
35024  * @cfg {Number} zoom default 15
35025  * @cfg {String} mapTypeId default google.maps.MapTypeId.ROADMAP
35026  * @cfg {Boolean} mapTypeControl default false
35027  * @cfg {Boolean} disableDoubleClickZoom default false
35028  * @cfg {Boolean} scrollwheel default true
35029  * @cfg {Boolean} streetViewControl default false
35030  * @cfg {Number} radius default 0
35031  * @cfg {String} locationName
35032  * @cfg {Boolean} draggable default true
35033  * @cfg {Boolean} enableAutocomplete default false
35034  * @cfg {Boolean} enableReverseGeocode default true
35035  * @cfg {String} markerTitle
35036  * 
35037  * @constructor
35038  * Create a new LocationPicker
35039  * @param {Object} config The config object
35040  */
35041
35042
35043 Roo.bootstrap.LocationPicker = function(config){
35044     
35045     Roo.bootstrap.LocationPicker.superclass.constructor.call(this, config);
35046     
35047     this.addEvents({
35048         /**
35049          * @event initial
35050          * Fires when the picker initialized.
35051          * @param {Roo.bootstrap.LocationPicker} this
35052          * @param {Google Location} location
35053          */
35054         initial : true,
35055         /**
35056          * @event positionchanged
35057          * Fires when the picker position changed.
35058          * @param {Roo.bootstrap.LocationPicker} this
35059          * @param {Google Location} location
35060          */
35061         positionchanged : true,
35062         /**
35063          * @event resize
35064          * Fires when the map resize.
35065          * @param {Roo.bootstrap.LocationPicker} this
35066          */
35067         resize : true,
35068         /**
35069          * @event show
35070          * Fires when the map show.
35071          * @param {Roo.bootstrap.LocationPicker} this
35072          */
35073         show : true,
35074         /**
35075          * @event hide
35076          * Fires when the map hide.
35077          * @param {Roo.bootstrap.LocationPicker} this
35078          */
35079         hide : true,
35080         /**
35081          * @event mapClick
35082          * Fires when click the map.
35083          * @param {Roo.bootstrap.LocationPicker} this
35084          * @param {Map event} e
35085          */
35086         mapClick : true,
35087         /**
35088          * @event mapRightClick
35089          * Fires when right click the map.
35090          * @param {Roo.bootstrap.LocationPicker} this
35091          * @param {Map event} e
35092          */
35093         mapRightClick : true,
35094         /**
35095          * @event markerClick
35096          * Fires when click the marker.
35097          * @param {Roo.bootstrap.LocationPicker} this
35098          * @param {Map event} e
35099          */
35100         markerClick : true,
35101         /**
35102          * @event markerRightClick
35103          * Fires when right click the marker.
35104          * @param {Roo.bootstrap.LocationPicker} this
35105          * @param {Map event} e
35106          */
35107         markerRightClick : true,
35108         /**
35109          * @event OverlayViewDraw
35110          * Fires when OverlayView Draw
35111          * @param {Roo.bootstrap.LocationPicker} this
35112          */
35113         OverlayViewDraw : true,
35114         /**
35115          * @event OverlayViewOnAdd
35116          * Fires when OverlayView Draw
35117          * @param {Roo.bootstrap.LocationPicker} this
35118          */
35119         OverlayViewOnAdd : true,
35120         /**
35121          * @event OverlayViewOnRemove
35122          * Fires when OverlayView Draw
35123          * @param {Roo.bootstrap.LocationPicker} this
35124          */
35125         OverlayViewOnRemove : true,
35126         /**
35127          * @event OverlayViewShow
35128          * Fires when OverlayView Draw
35129          * @param {Roo.bootstrap.LocationPicker} this
35130          * @param {Pixel} cpx
35131          */
35132         OverlayViewShow : true,
35133         /**
35134          * @event OverlayViewHide
35135          * Fires when OverlayView Draw
35136          * @param {Roo.bootstrap.LocationPicker} this
35137          */
35138         OverlayViewHide : true,
35139         /**
35140          * @event loadexception
35141          * Fires when load google lib failed.
35142          * @param {Roo.bootstrap.LocationPicker} this
35143          */
35144         loadexception : true
35145     });
35146         
35147 };
35148
35149 Roo.extend(Roo.bootstrap.LocationPicker, Roo.bootstrap.Component,  {
35150     
35151     gMapContext: false,
35152     
35153     latitude: 0,
35154     longitude: 0,
35155     zoom: 15,
35156     mapTypeId: false,
35157     mapTypeControl: false,
35158     disableDoubleClickZoom: false,
35159     scrollwheel: true,
35160     streetViewControl: false,
35161     radius: 0,
35162     locationName: '',
35163     draggable: true,
35164     enableAutocomplete: false,
35165     enableReverseGeocode: true,
35166     markerTitle: '',
35167     
35168     getAutoCreate: function()
35169     {
35170
35171         var cfg = {
35172             tag: 'div',
35173             cls: 'roo-location-picker'
35174         };
35175         
35176         return cfg
35177     },
35178     
35179     initEvents: function(ct, position)
35180     {       
35181         if(!this.el.getWidth() || this.isApplied()){
35182             return;
35183         }
35184         
35185         this.el.setVisibilityMode(Roo.Element.DISPLAY);
35186         
35187         this.initial();
35188     },
35189     
35190     initial: function()
35191     {
35192         if(typeof(google) == 'undefined' || typeof(google.maps) == 'undefined'){
35193             this.fireEvent('loadexception', this);
35194             return;
35195         }
35196         
35197         if(!this.mapTypeId){
35198             this.mapTypeId = google.maps.MapTypeId.ROADMAP;
35199         }
35200         
35201         this.gMapContext = this.GMapContext();
35202         
35203         this.initOverlayView();
35204         
35205         this.OverlayView = new Roo.bootstrap.LocationPicker.OverlayView(this.gMapContext.map);
35206         
35207         var _this = this;
35208                 
35209         google.maps.event.addListener(this.gMapContext.marker, "dragend", function(event) {
35210             _this.setPosition(_this.gMapContext.marker.position);
35211         });
35212         
35213         google.maps.event.addListener(this.gMapContext.map, 'click', function(event){
35214             _this.fireEvent('mapClick', this, event);
35215             
35216         });
35217
35218         google.maps.event.addListener(this.gMapContext.map, 'rightclick', function(event){
35219             _this.fireEvent('mapRightClick', this, event);
35220             
35221         });
35222         
35223         google.maps.event.addListener(this.gMapContext.marker, 'click', function(event){
35224             _this.fireEvent('markerClick', this, event);
35225             
35226         });
35227
35228         google.maps.event.addListener(this.gMapContext.marker, 'rightclick', function(event){
35229             _this.fireEvent('markerRightClick', this, event);
35230             
35231         });
35232         
35233         this.setPosition(this.gMapContext.location);
35234         
35235         this.fireEvent('initial', this, this.gMapContext.location);
35236     },
35237     
35238     initOverlayView: function()
35239     {
35240         var _this = this;
35241         
35242         Roo.bootstrap.LocationPicker.OverlayView.prototype = Roo.apply(new google.maps.OverlayView(), {
35243             
35244             draw: function()
35245             {
35246                 _this.fireEvent('OverlayViewDraw', _this);
35247             },
35248             
35249             onAdd: function()
35250             {
35251                 _this.fireEvent('OverlayViewOnAdd', _this);
35252             },
35253             
35254             onRemove: function()
35255             {
35256                 _this.fireEvent('OverlayViewOnRemove', _this);
35257             },
35258             
35259             show: function(cpx)
35260             {
35261                 _this.fireEvent('OverlayViewShow', _this, cpx);
35262             },
35263             
35264             hide: function()
35265             {
35266                 _this.fireEvent('OverlayViewHide', _this);
35267             }
35268             
35269         });
35270     },
35271     
35272     fromLatLngToContainerPixel: function(event)
35273     {
35274         return this.OverlayView.getProjection().fromLatLngToContainerPixel(event.latLng);
35275     },
35276     
35277     isApplied: function() 
35278     {
35279         return this.getGmapContext() == false ? false : true;
35280     },
35281     
35282     getGmapContext: function() 
35283     {
35284         return (typeof(this.gMapContext) == 'undefined') ? false : this.gMapContext;
35285     },
35286     
35287     GMapContext: function() 
35288     {
35289         var position = new google.maps.LatLng(this.latitude, this.longitude);
35290         
35291         var _map = new google.maps.Map(this.el.dom, {
35292             center: position,
35293             zoom: this.zoom,
35294             mapTypeId: this.mapTypeId,
35295             mapTypeControl: this.mapTypeControl,
35296             disableDoubleClickZoom: this.disableDoubleClickZoom,
35297             scrollwheel: this.scrollwheel,
35298             streetViewControl: this.streetViewControl,
35299             locationName: this.locationName,
35300             draggable: this.draggable,
35301             enableAutocomplete: this.enableAutocomplete,
35302             enableReverseGeocode: this.enableReverseGeocode
35303         });
35304         
35305         var _marker = new google.maps.Marker({
35306             position: position,
35307             map: _map,
35308             title: this.markerTitle,
35309             draggable: this.draggable
35310         });
35311         
35312         return {
35313             map: _map,
35314             marker: _marker,
35315             circle: null,
35316             location: position,
35317             radius: this.radius,
35318             locationName: this.locationName,
35319             addressComponents: {
35320                 formatted_address: null,
35321                 addressLine1: null,
35322                 addressLine2: null,
35323                 streetName: null,
35324                 streetNumber: null,
35325                 city: null,
35326                 district: null,
35327                 state: null,
35328                 stateOrProvince: null
35329             },
35330             settings: this,
35331             domContainer: this.el.dom,
35332             geodecoder: new google.maps.Geocoder()
35333         };
35334     },
35335     
35336     drawCircle: function(center, radius, options) 
35337     {
35338         if (this.gMapContext.circle != null) {
35339             this.gMapContext.circle.setMap(null);
35340         }
35341         if (radius > 0) {
35342             radius *= 1;
35343             options = Roo.apply({}, options, {
35344                 strokeColor: "#0000FF",
35345                 strokeOpacity: .35,
35346                 strokeWeight: 2,
35347                 fillColor: "#0000FF",
35348                 fillOpacity: .2
35349             });
35350             
35351             options.map = this.gMapContext.map;
35352             options.radius = radius;
35353             options.center = center;
35354             this.gMapContext.circle = new google.maps.Circle(options);
35355             return this.gMapContext.circle;
35356         }
35357         
35358         return null;
35359     },
35360     
35361     setPosition: function(location) 
35362     {
35363         this.gMapContext.location = location;
35364         this.gMapContext.marker.setPosition(location);
35365         this.gMapContext.map.panTo(location);
35366         this.drawCircle(location, this.gMapContext.radius, {});
35367         
35368         var _this = this;
35369         
35370         if (this.gMapContext.settings.enableReverseGeocode) {
35371             this.gMapContext.geodecoder.geocode({
35372                 latLng: this.gMapContext.location
35373             }, function(results, status) {
35374                 
35375                 if (status == google.maps.GeocoderStatus.OK && results.length > 0) {
35376                     _this.gMapContext.locationName = results[0].formatted_address;
35377                     _this.gMapContext.addressComponents = _this.address_component_from_google_geocode(results[0].address_components);
35378                     
35379                     _this.fireEvent('positionchanged', this, location);
35380                 }
35381             });
35382             
35383             return;
35384         }
35385         
35386         this.fireEvent('positionchanged', this, location);
35387     },
35388     
35389     resize: function()
35390     {
35391         google.maps.event.trigger(this.gMapContext.map, "resize");
35392         
35393         this.gMapContext.map.setCenter(this.gMapContext.marker.position);
35394         
35395         this.fireEvent('resize', this);
35396     },
35397     
35398     setPositionByLatLng: function(latitude, longitude)
35399     {
35400         this.setPosition(new google.maps.LatLng(latitude, longitude));
35401     },
35402     
35403     getCurrentPosition: function() 
35404     {
35405         return {
35406             latitude: this.gMapContext.location.lat(),
35407             longitude: this.gMapContext.location.lng()
35408         };
35409     },
35410     
35411     getAddressName: function() 
35412     {
35413         return this.gMapContext.locationName;
35414     },
35415     
35416     getAddressComponents: function() 
35417     {
35418         return this.gMapContext.addressComponents;
35419     },
35420     
35421     address_component_from_google_geocode: function(address_components) 
35422     {
35423         var result = {};
35424         
35425         for (var i = 0; i < address_components.length; i++) {
35426             var component = address_components[i];
35427             if (component.types.indexOf("postal_code") >= 0) {
35428                 result.postalCode = component.short_name;
35429             } else if (component.types.indexOf("street_number") >= 0) {
35430                 result.streetNumber = component.short_name;
35431             } else if (component.types.indexOf("route") >= 0) {
35432                 result.streetName = component.short_name;
35433             } else if (component.types.indexOf("neighborhood") >= 0) {
35434                 result.city = component.short_name;
35435             } else if (component.types.indexOf("locality") >= 0) {
35436                 result.city = component.short_name;
35437             } else if (component.types.indexOf("sublocality") >= 0) {
35438                 result.district = component.short_name;
35439             } else if (component.types.indexOf("administrative_area_level_1") >= 0) {
35440                 result.stateOrProvince = component.short_name;
35441             } else if (component.types.indexOf("country") >= 0) {
35442                 result.country = component.short_name;
35443             }
35444         }
35445         
35446         result.addressLine1 = [ result.streetNumber, result.streetName ].join(" ").trim();
35447         result.addressLine2 = "";
35448         return result;
35449     },
35450     
35451     setZoomLevel: function(zoom)
35452     {
35453         this.gMapContext.map.setZoom(zoom);
35454     },
35455     
35456     show: function()
35457     {
35458         if(!this.el){
35459             return;
35460         }
35461         
35462         this.el.show();
35463         
35464         this.resize();
35465         
35466         this.fireEvent('show', this);
35467     },
35468     
35469     hide: function()
35470     {
35471         if(!this.el){
35472             return;
35473         }
35474         
35475         this.el.hide();
35476         
35477         this.fireEvent('hide', this);
35478     }
35479     
35480 });
35481
35482 Roo.apply(Roo.bootstrap.LocationPicker, {
35483     
35484     OverlayView : function(map, options)
35485     {
35486         options = options || {};
35487         
35488         this.setMap(map);
35489     }
35490     
35491     
35492 });/**
35493  * @class Roo.bootstrap.Alert
35494  * @extends Roo.bootstrap.Component
35495  * Bootstrap Alert class - shows an alert area box
35496  * eg
35497  * <div class="alert alert-danger" role="alert"><span class="fa fa-exclamation-triangle"></span><span class="sr-only">Error:</span>
35498   Enter a valid email address
35499 </div>
35500  * @licence LGPL
35501  * @cfg {String} title The title of alert
35502  * @cfg {String} html The content of alert
35503  * @cfg {String} weight (success|info|warning|danger) Weight of the message
35504  * @cfg {String} fa font-awesomeicon
35505  * @cfg {Number} seconds default:-1 Number of seconds until it disapears (-1 means never.)
35506  * @cfg {Boolean} close true to show a x closer
35507  * 
35508  * 
35509  * @constructor
35510  * Create a new alert
35511  * @param {Object} config The config object
35512  */
35513
35514
35515 Roo.bootstrap.Alert = function(config){
35516     Roo.bootstrap.Alert.superclass.constructor.call(this, config);
35517     
35518 };
35519
35520 Roo.extend(Roo.bootstrap.Alert, Roo.bootstrap.Component,  {
35521     
35522     title: '',
35523     html: '',
35524     weight: false,
35525     fa: false,
35526     faicon: false, // BC
35527     close : false,
35528     
35529     
35530     getAutoCreate : function()
35531     {
35532         
35533         var cfg = {
35534             tag : 'div',
35535             cls : 'alert',
35536             cn : [
35537                 {
35538                     tag: 'button',
35539                     type :  "button",
35540                     cls: "close",
35541                     html : '×',
35542                     style : this.close ? '' : 'display:none'
35543                 },
35544                 {
35545                     tag : 'i',
35546                     cls : 'roo-alert-icon'
35547                     
35548                 },
35549                 {
35550                     tag : 'b',
35551                     cls : 'roo-alert-title',
35552                     html : this.title
35553                 },
35554                 {
35555                     tag : 'span',
35556                     cls : 'roo-alert-text',
35557                     html : this.html
35558                 }
35559             ]
35560         };
35561         
35562         if(this.faicon){
35563             cfg.cn[0].cls += ' fa ' + this.faicon;
35564         }
35565         if(this.fa){
35566             cfg.cn[0].cls += ' fa ' + this.fa;
35567         }
35568         
35569         if(this.weight){
35570             cfg.cls += ' alert-' + this.weight;
35571         }
35572         
35573         return cfg;
35574     },
35575     
35576     initEvents: function() 
35577     {
35578         this.el.setVisibilityMode(Roo.Element.DISPLAY);
35579         this.titleEl =  this.el.select('.roo-alert-title',true).first();
35580         this.iconEl = this.el.select('.roo-alert-icon',true).first();
35581         this.htmlEl = this.el.select('.roo-alert-text',true).first();
35582         if (this.seconds > 0) {
35583             this.hide.defer(this.seconds, this);
35584         }
35585     },
35586     /**
35587      * Set the Title Message HTML
35588      * @param {String} html
35589      */
35590     setTitle : function(str)
35591     {
35592         this.titleEl.dom.innerHTML = str;
35593     },
35594      
35595      /**
35596      * Set the Body Message HTML
35597      * @param {String} html
35598      */
35599     setHtml : function(str)
35600     {
35601         this.htmlEl.dom.innerHTML = str;
35602     },
35603     /**
35604      * Set the Weight of the alert
35605      * @param {String} (success|info|warning|danger) weight
35606      */
35607     
35608     setWeight : function(weight)
35609     {
35610         if(this.weight){
35611             this.el.removeClass('alert-' + this.weight);
35612         }
35613         
35614         this.weight = weight;
35615         
35616         this.el.addClass('alert-' + this.weight);
35617     },
35618       /**
35619      * Set the Icon of the alert
35620      * @param {String} see fontawsome names (name without the 'fa-' bit)
35621      */
35622     setIcon : function(icon)
35623     {
35624         if(this.faicon){
35625             this.alertEl.removeClass(['fa', 'fa-' + this.faicon]);
35626         }
35627         
35628         this.faicon = icon;
35629         
35630         this.alertEl.addClass(['fa', 'fa-' + this.faicon]);
35631     },
35632     /**
35633      * Hide the Alert
35634      */
35635     hide: function() 
35636     {
35637         this.el.hide();   
35638     },
35639     /**
35640      * Show the Alert
35641      */
35642     show: function() 
35643     {  
35644         this.el.show();   
35645     }
35646     
35647 });
35648
35649  
35650 /*
35651 * Licence: LGPL
35652 */
35653
35654 /**
35655  * @class Roo.bootstrap.UploadCropbox
35656  * @extends Roo.bootstrap.Component
35657  * Bootstrap UploadCropbox class
35658  * @cfg {String} emptyText show when image has been loaded
35659  * @cfg {String} rotateNotify show when image too small to rotate
35660  * @cfg {Number} errorTimeout default 3000
35661  * @cfg {Number} minWidth default 300
35662  * @cfg {Number} minHeight default 300
35663  * @cfg {Array} buttons default ['rotateLeft', 'pictureBtn', 'rotateRight']
35664  * @cfg {Boolean} isDocument (true|false) default false
35665  * @cfg {String} url action url
35666  * @cfg {String} paramName default 'imageUpload'
35667  * @cfg {String} method default POST
35668  * @cfg {Boolean} loadMask (true|false) default true
35669  * @cfg {Boolean} loadingText default 'Loading...'
35670  * 
35671  * @constructor
35672  * Create a new UploadCropbox
35673  * @param {Object} config The config object
35674  */
35675
35676 Roo.bootstrap.UploadCropbox = function(config){
35677     Roo.bootstrap.UploadCropbox.superclass.constructor.call(this, config);
35678     
35679     this.addEvents({
35680         /**
35681          * @event beforeselectfile
35682          * Fire before select file
35683          * @param {Roo.bootstrap.UploadCropbox} this
35684          */
35685         "beforeselectfile" : true,
35686         /**
35687          * @event initial
35688          * Fire after initEvent
35689          * @param {Roo.bootstrap.UploadCropbox} this
35690          */
35691         "initial" : true,
35692         /**
35693          * @event crop
35694          * Fire after initEvent
35695          * @param {Roo.bootstrap.UploadCropbox} this
35696          * @param {String} data
35697          */
35698         "crop" : true,
35699         /**
35700          * @event prepare
35701          * Fire when preparing the file data
35702          * @param {Roo.bootstrap.UploadCropbox} this
35703          * @param {Object} file
35704          */
35705         "prepare" : true,
35706         /**
35707          * @event exception
35708          * Fire when get exception
35709          * @param {Roo.bootstrap.UploadCropbox} this
35710          * @param {XMLHttpRequest} xhr
35711          */
35712         "exception" : true,
35713         /**
35714          * @event beforeloadcanvas
35715          * Fire before load the canvas
35716          * @param {Roo.bootstrap.UploadCropbox} this
35717          * @param {String} src
35718          */
35719         "beforeloadcanvas" : true,
35720         /**
35721          * @event trash
35722          * Fire when trash image
35723          * @param {Roo.bootstrap.UploadCropbox} this
35724          */
35725         "trash" : true,
35726         /**
35727          * @event download
35728          * Fire when download the image
35729          * @param {Roo.bootstrap.UploadCropbox} this
35730          */
35731         "download" : true,
35732         /**
35733          * @event footerbuttonclick
35734          * Fire when footerbuttonclick
35735          * @param {Roo.bootstrap.UploadCropbox} this
35736          * @param {String} type
35737          */
35738         "footerbuttonclick" : true,
35739         /**
35740          * @event resize
35741          * Fire when resize
35742          * @param {Roo.bootstrap.UploadCropbox} this
35743          */
35744         "resize" : true,
35745         /**
35746          * @event rotate
35747          * Fire when rotate the image
35748          * @param {Roo.bootstrap.UploadCropbox} this
35749          * @param {String} pos
35750          */
35751         "rotate" : true,
35752         /**
35753          * @event inspect
35754          * Fire when inspect the file
35755          * @param {Roo.bootstrap.UploadCropbox} this
35756          * @param {Object} file
35757          */
35758         "inspect" : true,
35759         /**
35760          * @event upload
35761          * Fire when xhr upload the file
35762          * @param {Roo.bootstrap.UploadCropbox} this
35763          * @param {Object} data
35764          */
35765         "upload" : true,
35766         /**
35767          * @event arrange
35768          * Fire when arrange the file data
35769          * @param {Roo.bootstrap.UploadCropbox} this
35770          * @param {Object} formData
35771          */
35772         "arrange" : true
35773     });
35774     
35775     this.buttons = this.buttons || Roo.bootstrap.UploadCropbox.footer.STANDARD;
35776 };
35777
35778 Roo.extend(Roo.bootstrap.UploadCropbox, Roo.bootstrap.Component,  {
35779     
35780     emptyText : 'Click to upload image',
35781     rotateNotify : 'Image is too small to rotate',
35782     errorTimeout : 3000,
35783     scale : 0,
35784     baseScale : 1,
35785     rotate : 0,
35786     dragable : false,
35787     pinching : false,
35788     mouseX : 0,
35789     mouseY : 0,
35790     cropData : false,
35791     minWidth : 300,
35792     minHeight : 300,
35793     file : false,
35794     exif : {},
35795     baseRotate : 1,
35796     cropType : 'image/jpeg',
35797     buttons : false,
35798     canvasLoaded : false,
35799     isDocument : false,
35800     method : 'POST',
35801     paramName : 'imageUpload',
35802     loadMask : true,
35803     loadingText : 'Loading...',
35804     maskEl : false,
35805     
35806     getAutoCreate : function()
35807     {
35808         var cfg = {
35809             tag : 'div',
35810             cls : 'roo-upload-cropbox',
35811             cn : [
35812                 {
35813                     tag : 'input',
35814                     cls : 'roo-upload-cropbox-selector',
35815                     type : 'file'
35816                 },
35817                 {
35818                     tag : 'div',
35819                     cls : 'roo-upload-cropbox-body',
35820                     style : 'cursor:pointer',
35821                     cn : [
35822                         {
35823                             tag : 'div',
35824                             cls : 'roo-upload-cropbox-preview'
35825                         },
35826                         {
35827                             tag : 'div',
35828                             cls : 'roo-upload-cropbox-thumb'
35829                         },
35830                         {
35831                             tag : 'div',
35832                             cls : 'roo-upload-cropbox-empty-notify',
35833                             html : this.emptyText
35834                         },
35835                         {
35836                             tag : 'div',
35837                             cls : 'roo-upload-cropbox-error-notify alert alert-danger',
35838                             html : this.rotateNotify
35839                         }
35840                     ]
35841                 },
35842                 {
35843                     tag : 'div',
35844                     cls : 'roo-upload-cropbox-footer',
35845                     cn : {
35846                         tag : 'div',
35847                         cls : 'btn-group btn-group-justified roo-upload-cropbox-btn-group',
35848                         cn : []
35849                     }
35850                 }
35851             ]
35852         };
35853         
35854         return cfg;
35855     },
35856     
35857     onRender : function(ct, position)
35858     {
35859         Roo.bootstrap.UploadCropbox.superclass.onRender.call(this, ct, position);
35860         
35861         if (this.buttons.length) {
35862             
35863             Roo.each(this.buttons, function(bb) {
35864                 
35865                 var btn = this.el.select('.roo-upload-cropbox-footer div.roo-upload-cropbox-btn-group').first().createChild(bb);
35866                 
35867                 btn.on('click', this.onFooterButtonClick.createDelegate(this, [bb.action], true));
35868                 
35869             }, this);
35870         }
35871         
35872         if(this.loadMask){
35873             this.maskEl = this.el;
35874         }
35875     },
35876     
35877     initEvents : function()
35878     {
35879         this.urlAPI = (window.createObjectURL && window) || 
35880                                 (window.URL && URL.revokeObjectURL && URL) || 
35881                                 (window.webkitURL && webkitURL);
35882                         
35883         this.bodyEl = this.el.select('.roo-upload-cropbox-body', true).first();
35884         this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
35885         
35886         this.selectorEl = this.el.select('.roo-upload-cropbox-selector', true).first();
35887         this.selectorEl.hide();
35888         
35889         this.previewEl = this.el.select('.roo-upload-cropbox-preview', true).first();
35890         this.previewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
35891         
35892         this.thumbEl = this.el.select('.roo-upload-cropbox-thumb', true).first();
35893         this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
35894         this.thumbEl.hide();
35895         
35896         this.notifyEl = this.el.select('.roo-upload-cropbox-empty-notify', true).first();
35897         this.notifyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
35898         
35899         this.errorEl = this.el.select('.roo-upload-cropbox-error-notify', true).first();
35900         this.errorEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
35901         this.errorEl.hide();
35902         
35903         this.footerEl = this.el.select('.roo-upload-cropbox-footer', true).first();
35904         this.footerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
35905         this.footerEl.hide();
35906         
35907         this.setThumbBoxSize();
35908         
35909         this.bind();
35910         
35911         this.resize();
35912         
35913         this.fireEvent('initial', this);
35914     },
35915
35916     bind : function()
35917     {
35918         var _this = this;
35919         
35920         window.addEventListener("resize", function() { _this.resize(); } );
35921         
35922         this.bodyEl.on('click', this.beforeSelectFile, this);
35923         
35924         if(Roo.isTouch){
35925             this.bodyEl.on('touchstart', this.onTouchStart, this);
35926             this.bodyEl.on('touchmove', this.onTouchMove, this);
35927             this.bodyEl.on('touchend', this.onTouchEnd, this);
35928         }
35929         
35930         if(!Roo.isTouch){
35931             this.bodyEl.on('mousedown', this.onMouseDown, this);
35932             this.bodyEl.on('mousemove', this.onMouseMove, this);
35933             var mousewheel = (/Firefox/i.test(navigator.userAgent))? 'DOMMouseScroll' : 'mousewheel';
35934             this.bodyEl.on(mousewheel, this.onMouseWheel, this);
35935             Roo.get(document).on('mouseup', this.onMouseUp, this);
35936         }
35937         
35938         this.selectorEl.on('change', this.onFileSelected, this);
35939     },
35940     
35941     reset : function()
35942     {    
35943         this.scale = 0;
35944         this.baseScale = 1;
35945         this.rotate = 0;
35946         this.baseRotate = 1;
35947         this.dragable = false;
35948         this.pinching = false;
35949         this.mouseX = 0;
35950         this.mouseY = 0;
35951         this.cropData = false;
35952         this.notifyEl.dom.innerHTML = this.emptyText;
35953         
35954         this.selectorEl.dom.value = '';
35955         
35956     },
35957     
35958     resize : function()
35959     {
35960         if(this.fireEvent('resize', this) != false){
35961             this.setThumbBoxPosition();
35962             this.setCanvasPosition();
35963         }
35964     },
35965     
35966     onFooterButtonClick : function(e, el, o, type)
35967     {
35968         switch (type) {
35969             case 'rotate-left' :
35970                 this.onRotateLeft(e);
35971                 break;
35972             case 'rotate-right' :
35973                 this.onRotateRight(e);
35974                 break;
35975             case 'picture' :
35976                 this.beforeSelectFile(e);
35977                 break;
35978             case 'trash' :
35979                 this.trash(e);
35980                 break;
35981             case 'crop' :
35982                 this.crop(e);
35983                 break;
35984             case 'download' :
35985                 this.download(e);
35986                 break;
35987             default :
35988                 break;
35989         }
35990         
35991         this.fireEvent('footerbuttonclick', this, type);
35992     },
35993     
35994     beforeSelectFile : function(e)
35995     {
35996         e.preventDefault();
35997         
35998         if(this.fireEvent('beforeselectfile', this) != false){
35999             this.selectorEl.dom.click();
36000         }
36001     },
36002     
36003     onFileSelected : function(e)
36004     {
36005         e.preventDefault();
36006         
36007         if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
36008             return;
36009         }
36010         
36011         var file = this.selectorEl.dom.files[0];
36012         
36013         if(this.fireEvent('inspect', this, file) != false){
36014             this.prepare(file);
36015         }
36016         
36017     },
36018     
36019     trash : function(e)
36020     {
36021         this.fireEvent('trash', this);
36022     },
36023     
36024     download : function(e)
36025     {
36026         this.fireEvent('download', this);
36027     },
36028     
36029     loadCanvas : function(src)
36030     {   
36031         if(this.fireEvent('beforeloadcanvas', this, src) != false){
36032             
36033             this.reset();
36034             
36035             this.imageEl = document.createElement('img');
36036             
36037             var _this = this;
36038             
36039             this.imageEl.addEventListener("load", function(){ _this.onLoadCanvas(); });
36040             
36041             this.imageEl.src = src;
36042         }
36043     },
36044     
36045     onLoadCanvas : function()
36046     {   
36047         this.imageEl.OriginWidth = this.imageEl.naturalWidth || this.imageEl.width;
36048         this.imageEl.OriginHeight = this.imageEl.naturalHeight || this.imageEl.height;
36049         
36050         this.bodyEl.un('click', this.beforeSelectFile, this);
36051         
36052         this.notifyEl.hide();
36053         this.thumbEl.show();
36054         this.footerEl.show();
36055         
36056         this.baseRotateLevel();
36057         
36058         if(this.isDocument){
36059             this.setThumbBoxSize();
36060         }
36061         
36062         this.setThumbBoxPosition();
36063         
36064         this.baseScaleLevel();
36065         
36066         this.draw();
36067         
36068         this.resize();
36069         
36070         this.canvasLoaded = true;
36071         
36072         if(this.loadMask){
36073             this.maskEl.unmask();
36074         }
36075         
36076     },
36077     
36078     setCanvasPosition : function()
36079     {   
36080         if(!this.canvasEl){
36081             return;
36082         }
36083         
36084         var pw = Math.ceil((this.bodyEl.getWidth() - this.canvasEl.width) / 2);
36085         var ph = Math.ceil((this.bodyEl.getHeight() - this.canvasEl.height) / 2);
36086         
36087         this.previewEl.setLeft(pw);
36088         this.previewEl.setTop(ph);
36089         
36090     },
36091     
36092     onMouseDown : function(e)
36093     {   
36094         e.stopEvent();
36095         
36096         this.dragable = true;
36097         this.pinching = false;
36098         
36099         if(this.isDocument && (this.canvasEl.width < this.thumbEl.getWidth() || this.canvasEl.height < this.thumbEl.getHeight())){
36100             this.dragable = false;
36101             return;
36102         }
36103         
36104         this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
36105         this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
36106         
36107     },
36108     
36109     onMouseMove : function(e)
36110     {   
36111         e.stopEvent();
36112         
36113         if(!this.canvasLoaded){
36114             return;
36115         }
36116         
36117         if (!this.dragable){
36118             return;
36119         }
36120         
36121         var minX = Math.ceil(this.thumbEl.getLeft(true));
36122         var minY = Math.ceil(this.thumbEl.getTop(true));
36123         
36124         var maxX = Math.ceil(minX + this.thumbEl.getWidth() - this.canvasEl.width);
36125         var maxY = Math.ceil(minY + this.thumbEl.getHeight() - this.canvasEl.height);
36126         
36127         var x = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
36128         var y = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
36129         
36130         x = x - this.mouseX;
36131         y = y - this.mouseY;
36132         
36133         var bgX = Math.ceil(x + this.previewEl.getLeft(true));
36134         var bgY = Math.ceil(y + this.previewEl.getTop(true));
36135         
36136         bgX = (minX < bgX) ? minX : ((maxX > bgX) ? maxX : bgX);
36137         bgY = (minY < bgY) ? minY : ((maxY > bgY) ? maxY : bgY);
36138         
36139         this.previewEl.setLeft(bgX);
36140         this.previewEl.setTop(bgY);
36141         
36142         this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
36143         this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
36144     },
36145     
36146     onMouseUp : function(e)
36147     {   
36148         e.stopEvent();
36149         
36150         this.dragable = false;
36151     },
36152     
36153     onMouseWheel : function(e)
36154     {   
36155         e.stopEvent();
36156         
36157         this.startScale = this.scale;
36158         
36159         this.scale = (e.getWheelDelta() == 1) ? (this.scale + 1) : (this.scale - 1);
36160         
36161         if(!this.zoomable()){
36162             this.scale = this.startScale;
36163             return;
36164         }
36165         
36166         this.draw();
36167         
36168         return;
36169     },
36170     
36171     zoomable : function()
36172     {
36173         var minScale = this.thumbEl.getWidth() / this.minWidth;
36174         
36175         if(this.minWidth < this.minHeight){
36176             minScale = this.thumbEl.getHeight() / this.minHeight;
36177         }
36178         
36179         var width = Math.ceil(this.imageEl.OriginWidth * this.getScaleLevel() / minScale);
36180         var height = Math.ceil(this.imageEl.OriginHeight * this.getScaleLevel() / minScale);
36181         
36182         if(
36183                 this.isDocument &&
36184                 (this.rotate == 0 || this.rotate == 180) && 
36185                 (
36186                     width > this.imageEl.OriginWidth || 
36187                     height > this.imageEl.OriginHeight ||
36188                     (width < this.minWidth && height < this.minHeight)
36189                 )
36190         ){
36191             return false;
36192         }
36193         
36194         if(
36195                 this.isDocument &&
36196                 (this.rotate == 90 || this.rotate == 270) && 
36197                 (
36198                     width > this.imageEl.OriginWidth || 
36199                     height > this.imageEl.OriginHeight ||
36200                     (width < this.minHeight && height < this.minWidth)
36201                 )
36202         ){
36203             return false;
36204         }
36205         
36206         if(
36207                 !this.isDocument &&
36208                 (this.rotate == 0 || this.rotate == 180) && 
36209                 (
36210                     width < this.minWidth || 
36211                     width > this.imageEl.OriginWidth || 
36212                     height < this.minHeight || 
36213                     height > this.imageEl.OriginHeight
36214                 )
36215         ){
36216             return false;
36217         }
36218         
36219         if(
36220                 !this.isDocument &&
36221                 (this.rotate == 90 || this.rotate == 270) && 
36222                 (
36223                     width < this.minHeight || 
36224                     width > this.imageEl.OriginWidth || 
36225                     height < this.minWidth || 
36226                     height > this.imageEl.OriginHeight
36227                 )
36228         ){
36229             return false;
36230         }
36231         
36232         return true;
36233         
36234     },
36235     
36236     onRotateLeft : function(e)
36237     {   
36238         if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
36239             
36240             var minScale = this.thumbEl.getWidth() / this.minWidth;
36241             
36242             var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
36243             var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
36244             
36245             this.startScale = this.scale;
36246             
36247             while (this.getScaleLevel() < minScale){
36248             
36249                 this.scale = this.scale + 1;
36250                 
36251                 if(!this.zoomable()){
36252                     break;
36253                 }
36254                 
36255                 if(
36256                         Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
36257                         Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
36258                 ){
36259                     continue;
36260                 }
36261                 
36262                 this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
36263
36264                 this.draw();
36265                 
36266                 return;
36267             }
36268             
36269             this.scale = this.startScale;
36270             
36271             this.onRotateFail();
36272             
36273             return false;
36274         }
36275         
36276         this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
36277
36278         if(this.isDocument){
36279             this.setThumbBoxSize();
36280             this.setThumbBoxPosition();
36281             this.setCanvasPosition();
36282         }
36283         
36284         this.draw();
36285         
36286         this.fireEvent('rotate', this, 'left');
36287         
36288     },
36289     
36290     onRotateRight : function(e)
36291     {
36292         if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
36293             
36294             var minScale = this.thumbEl.getWidth() / this.minWidth;
36295         
36296             var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
36297             var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
36298             
36299             this.startScale = this.scale;
36300             
36301             while (this.getScaleLevel() < minScale){
36302             
36303                 this.scale = this.scale + 1;
36304                 
36305                 if(!this.zoomable()){
36306                     break;
36307                 }
36308                 
36309                 if(
36310                         Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
36311                         Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
36312                 ){
36313                     continue;
36314                 }
36315                 
36316                 this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
36317
36318                 this.draw();
36319                 
36320                 return;
36321             }
36322             
36323             this.scale = this.startScale;
36324             
36325             this.onRotateFail();
36326             
36327             return false;
36328         }
36329         
36330         this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
36331
36332         if(this.isDocument){
36333             this.setThumbBoxSize();
36334             this.setThumbBoxPosition();
36335             this.setCanvasPosition();
36336         }
36337         
36338         this.draw();
36339         
36340         this.fireEvent('rotate', this, 'right');
36341     },
36342     
36343     onRotateFail : function()
36344     {
36345         this.errorEl.show(true);
36346         
36347         var _this = this;
36348         
36349         (function() { _this.errorEl.hide(true); }).defer(this.errorTimeout);
36350     },
36351     
36352     draw : function()
36353     {
36354         this.previewEl.dom.innerHTML = '';
36355         
36356         var canvasEl = document.createElement("canvas");
36357         
36358         var contextEl = canvasEl.getContext("2d");
36359         
36360         canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
36361         canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
36362         var center = this.imageEl.OriginWidth / 2;
36363         
36364         if(this.imageEl.OriginWidth < this.imageEl.OriginHeight){
36365             canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
36366             canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
36367             center = this.imageEl.OriginHeight / 2;
36368         }
36369         
36370         contextEl.scale(this.getScaleLevel(), this.getScaleLevel());
36371         
36372         contextEl.translate(center, center);
36373         contextEl.rotate(this.rotate * Math.PI / 180);
36374
36375         contextEl.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
36376         
36377         this.canvasEl = document.createElement("canvas");
36378         
36379         this.contextEl = this.canvasEl.getContext("2d");
36380         
36381         switch (this.rotate) {
36382             case 0 :
36383                 
36384                 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
36385                 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
36386                 
36387                 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
36388                 
36389                 break;
36390             case 90 : 
36391                 
36392                 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
36393                 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
36394                 
36395                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
36396                     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);
36397                     break;
36398                 }
36399                 
36400                 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
36401                 
36402                 break;
36403             case 180 :
36404                 
36405                 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
36406                 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
36407                 
36408                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
36409                     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);
36410                     break;
36411                 }
36412                 
36413                 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);
36414                 
36415                 break;
36416             case 270 :
36417                 
36418                 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
36419                 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
36420         
36421                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
36422                     this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
36423                     break;
36424                 }
36425                 
36426                 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);
36427                 
36428                 break;
36429             default : 
36430                 break;
36431         }
36432         
36433         this.previewEl.appendChild(this.canvasEl);
36434         
36435         this.setCanvasPosition();
36436     },
36437     
36438     crop : function()
36439     {
36440         if(!this.canvasLoaded){
36441             return;
36442         }
36443         
36444         var imageCanvas = document.createElement("canvas");
36445         
36446         var imageContext = imageCanvas.getContext("2d");
36447         
36448         imageCanvas.width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
36449         imageCanvas.height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
36450         
36451         var center = imageCanvas.width / 2;
36452         
36453         imageContext.translate(center, center);
36454         
36455         imageContext.rotate(this.rotate * Math.PI / 180);
36456         
36457         imageContext.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
36458         
36459         var canvas = document.createElement("canvas");
36460         
36461         var context = canvas.getContext("2d");
36462                 
36463         canvas.width = this.minWidth;
36464         canvas.height = this.minHeight;
36465
36466         switch (this.rotate) {
36467             case 0 :
36468                 
36469                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
36470                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
36471                 
36472                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
36473                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
36474                 
36475                 var targetWidth = this.minWidth - 2 * x;
36476                 var targetHeight = this.minHeight - 2 * y;
36477                 
36478                 var scale = 1;
36479                 
36480                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
36481                     scale = targetWidth / width;
36482                 }
36483                 
36484                 if(x > 0 && y == 0){
36485                     scale = targetHeight / height;
36486                 }
36487                 
36488                 if(x > 0 && y > 0){
36489                     scale = targetWidth / width;
36490                     
36491                     if(width < height){
36492                         scale = targetHeight / height;
36493                     }
36494                 }
36495                 
36496                 context.scale(scale, scale);
36497                 
36498                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
36499                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
36500
36501                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
36502                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
36503
36504                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
36505                 
36506                 break;
36507             case 90 : 
36508                 
36509                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
36510                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
36511                 
36512                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
36513                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
36514                 
36515                 var targetWidth = this.minWidth - 2 * x;
36516                 var targetHeight = this.minHeight - 2 * y;
36517                 
36518                 var scale = 1;
36519                 
36520                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
36521                     scale = targetWidth / width;
36522                 }
36523                 
36524                 if(x > 0 && y == 0){
36525                     scale = targetHeight / height;
36526                 }
36527                 
36528                 if(x > 0 && y > 0){
36529                     scale = targetWidth / width;
36530                     
36531                     if(width < height){
36532                         scale = targetHeight / height;
36533                     }
36534                 }
36535                 
36536                 context.scale(scale, scale);
36537                 
36538                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
36539                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
36540
36541                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
36542                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
36543                 
36544                 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
36545                 
36546                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
36547                 
36548                 break;
36549             case 180 :
36550                 
36551                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
36552                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
36553                 
36554                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
36555                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
36556                 
36557                 var targetWidth = this.minWidth - 2 * x;
36558                 var targetHeight = this.minHeight - 2 * y;
36559                 
36560                 var scale = 1;
36561                 
36562                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
36563                     scale = targetWidth / width;
36564                 }
36565                 
36566                 if(x > 0 && y == 0){
36567                     scale = targetHeight / height;
36568                 }
36569                 
36570                 if(x > 0 && y > 0){
36571                     scale = targetWidth / width;
36572                     
36573                     if(width < height){
36574                         scale = targetHeight / height;
36575                     }
36576                 }
36577                 
36578                 context.scale(scale, scale);
36579                 
36580                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
36581                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
36582
36583                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
36584                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
36585
36586                 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
36587                 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
36588                 
36589                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
36590                 
36591                 break;
36592             case 270 :
36593                 
36594                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
36595                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
36596                 
36597                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
36598                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
36599                 
36600                 var targetWidth = this.minWidth - 2 * x;
36601                 var targetHeight = this.minHeight - 2 * y;
36602                 
36603                 var scale = 1;
36604                 
36605                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
36606                     scale = targetWidth / width;
36607                 }
36608                 
36609                 if(x > 0 && y == 0){
36610                     scale = targetHeight / height;
36611                 }
36612                 
36613                 if(x > 0 && y > 0){
36614                     scale = targetWidth / width;
36615                     
36616                     if(width < height){
36617                         scale = targetHeight / height;
36618                     }
36619                 }
36620                 
36621                 context.scale(scale, scale);
36622                 
36623                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
36624                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
36625
36626                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
36627                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
36628                 
36629                 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
36630                 
36631                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
36632                 
36633                 break;
36634             default : 
36635                 break;
36636         }
36637         
36638         this.cropData = canvas.toDataURL(this.cropType);
36639         
36640         if(this.fireEvent('crop', this, this.cropData) !== false){
36641             this.process(this.file, this.cropData);
36642         }
36643         
36644         return;
36645         
36646     },
36647     
36648     setThumbBoxSize : function()
36649     {
36650         var width, height;
36651         
36652         if(this.isDocument && typeof(this.imageEl) != 'undefined'){
36653             width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.max(this.minWidth, this.minHeight) : Math.min(this.minWidth, this.minHeight);
36654             height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.min(this.minWidth, this.minHeight) : Math.max(this.minWidth, this.minHeight);
36655             
36656             this.minWidth = width;
36657             this.minHeight = height;
36658             
36659             if(this.rotate == 90 || this.rotate == 270){
36660                 this.minWidth = height;
36661                 this.minHeight = width;
36662             }
36663         }
36664         
36665         height = 300;
36666         width = Math.ceil(this.minWidth * height / this.minHeight);
36667         
36668         if(this.minWidth > this.minHeight){
36669             width = 300;
36670             height = Math.ceil(this.minHeight * width / this.minWidth);
36671         }
36672         
36673         this.thumbEl.setStyle({
36674             width : width + 'px',
36675             height : height + 'px'
36676         });
36677
36678         return;
36679             
36680     },
36681     
36682     setThumbBoxPosition : function()
36683     {
36684         var x = Math.ceil((this.bodyEl.getWidth() - this.thumbEl.getWidth()) / 2 );
36685         var y = Math.ceil((this.bodyEl.getHeight() - this.thumbEl.getHeight()) / 2);
36686         
36687         this.thumbEl.setLeft(x);
36688         this.thumbEl.setTop(y);
36689         
36690     },
36691     
36692     baseRotateLevel : function()
36693     {
36694         this.baseRotate = 1;
36695         
36696         if(
36697                 typeof(this.exif) != 'undefined' &&
36698                 typeof(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != 'undefined' &&
36699                 [1, 3, 6, 8].indexOf(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != -1
36700         ){
36701             this.baseRotate = this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']];
36702         }
36703         
36704         this.rotate = Roo.bootstrap.UploadCropbox['Orientation'][this.baseRotate];
36705         
36706     },
36707     
36708     baseScaleLevel : function()
36709     {
36710         var width, height;
36711         
36712         if(this.isDocument){
36713             
36714             if(this.baseRotate == 6 || this.baseRotate == 8){
36715             
36716                 height = this.thumbEl.getHeight();
36717                 this.baseScale = height / this.imageEl.OriginWidth;
36718
36719                 if(this.imageEl.OriginHeight * this.baseScale > this.thumbEl.getWidth()){
36720                     width = this.thumbEl.getWidth();
36721                     this.baseScale = width / this.imageEl.OriginHeight;
36722                 }
36723
36724                 return;
36725             }
36726
36727             height = this.thumbEl.getHeight();
36728             this.baseScale = height / this.imageEl.OriginHeight;
36729
36730             if(this.imageEl.OriginWidth * this.baseScale > this.thumbEl.getWidth()){
36731                 width = this.thumbEl.getWidth();
36732                 this.baseScale = width / this.imageEl.OriginWidth;
36733             }
36734
36735             return;
36736         }
36737         
36738         if(this.baseRotate == 6 || this.baseRotate == 8){
36739             
36740             width = this.thumbEl.getHeight();
36741             this.baseScale = width / this.imageEl.OriginHeight;
36742             
36743             if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getWidth()){
36744                 height = this.thumbEl.getWidth();
36745                 this.baseScale = height / this.imageEl.OriginHeight;
36746             }
36747             
36748             if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
36749                 height = this.thumbEl.getWidth();
36750                 this.baseScale = height / this.imageEl.OriginHeight;
36751                 
36752                 if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getHeight()){
36753                     width = this.thumbEl.getHeight();
36754                     this.baseScale = width / this.imageEl.OriginWidth;
36755                 }
36756             }
36757             
36758             return;
36759         }
36760         
36761         width = this.thumbEl.getWidth();
36762         this.baseScale = width / this.imageEl.OriginWidth;
36763         
36764         if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getHeight()){
36765             height = this.thumbEl.getHeight();
36766             this.baseScale = height / this.imageEl.OriginHeight;
36767         }
36768         
36769         if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
36770             
36771             height = this.thumbEl.getHeight();
36772             this.baseScale = height / this.imageEl.OriginHeight;
36773             
36774             if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getWidth()){
36775                 width = this.thumbEl.getWidth();
36776                 this.baseScale = width / this.imageEl.OriginWidth;
36777             }
36778             
36779         }
36780         
36781         return;
36782     },
36783     
36784     getScaleLevel : function()
36785     {
36786         return this.baseScale * Math.pow(1.1, this.scale);
36787     },
36788     
36789     onTouchStart : function(e)
36790     {
36791         if(!this.canvasLoaded){
36792             this.beforeSelectFile(e);
36793             return;
36794         }
36795         
36796         var touches = e.browserEvent.touches;
36797         
36798         if(!touches){
36799             return;
36800         }
36801         
36802         if(touches.length == 1){
36803             this.onMouseDown(e);
36804             return;
36805         }
36806         
36807         if(touches.length != 2){
36808             return;
36809         }
36810         
36811         var coords = [];
36812         
36813         for(var i = 0, finger; finger = touches[i]; i++){
36814             coords.push(finger.pageX, finger.pageY);
36815         }
36816         
36817         var x = Math.pow(coords[0] - coords[2], 2);
36818         var y = Math.pow(coords[1] - coords[3], 2);
36819         
36820         this.startDistance = Math.sqrt(x + y);
36821         
36822         this.startScale = this.scale;
36823         
36824         this.pinching = true;
36825         this.dragable = false;
36826         
36827     },
36828     
36829     onTouchMove : function(e)
36830     {
36831         if(!this.pinching && !this.dragable){
36832             return;
36833         }
36834         
36835         var touches = e.browserEvent.touches;
36836         
36837         if(!touches){
36838             return;
36839         }
36840         
36841         if(this.dragable){
36842             this.onMouseMove(e);
36843             return;
36844         }
36845         
36846         var coords = [];
36847         
36848         for(var i = 0, finger; finger = touches[i]; i++){
36849             coords.push(finger.pageX, finger.pageY);
36850         }
36851         
36852         var x = Math.pow(coords[0] - coords[2], 2);
36853         var y = Math.pow(coords[1] - coords[3], 2);
36854         
36855         this.endDistance = Math.sqrt(x + y);
36856         
36857         this.scale = this.startScale + Math.floor(Math.log(this.endDistance / this.startDistance) / Math.log(1.1));
36858         
36859         if(!this.zoomable()){
36860             this.scale = this.startScale;
36861             return;
36862         }
36863         
36864         this.draw();
36865         
36866     },
36867     
36868     onTouchEnd : function(e)
36869     {
36870         this.pinching = false;
36871         this.dragable = false;
36872         
36873     },
36874     
36875     process : function(file, crop)
36876     {
36877         if(this.loadMask){
36878             this.maskEl.mask(this.loadingText);
36879         }
36880         
36881         this.xhr = new XMLHttpRequest();
36882         
36883         file.xhr = this.xhr;
36884
36885         this.xhr.open(this.method, this.url, true);
36886         
36887         var headers = {
36888             "Accept": "application/json",
36889             "Cache-Control": "no-cache",
36890             "X-Requested-With": "XMLHttpRequest"
36891         };
36892         
36893         for (var headerName in headers) {
36894             var headerValue = headers[headerName];
36895             if (headerValue) {
36896                 this.xhr.setRequestHeader(headerName, headerValue);
36897             }
36898         }
36899         
36900         var _this = this;
36901         
36902         this.xhr.onload = function()
36903         {
36904             _this.xhrOnLoad(_this.xhr);
36905         }
36906         
36907         this.xhr.onerror = function()
36908         {
36909             _this.xhrOnError(_this.xhr);
36910         }
36911         
36912         var formData = new FormData();
36913
36914         formData.append('returnHTML', 'NO');
36915         
36916         if(crop){
36917             formData.append('crop', crop);
36918         }
36919         
36920         if(typeof(file) != 'undefined' && (typeof(file.id) == 'undefined' || file.id * 1 < 1)){
36921             formData.append(this.paramName, file, file.name);
36922         }
36923         
36924         if(typeof(file.filename) != 'undefined'){
36925             formData.append('filename', file.filename);
36926         }
36927         
36928         if(typeof(file.mimetype) != 'undefined'){
36929             formData.append('mimetype', file.mimetype);
36930         }
36931         
36932         if(this.fireEvent('arrange', this, formData) != false){
36933             this.xhr.send(formData);
36934         };
36935     },
36936     
36937     xhrOnLoad : function(xhr)
36938     {
36939         if(this.loadMask){
36940             this.maskEl.unmask();
36941         }
36942         
36943         if (xhr.readyState !== 4) {
36944             this.fireEvent('exception', this, xhr);
36945             return;
36946         }
36947
36948         var response = Roo.decode(xhr.responseText);
36949         
36950         if(!response.success){
36951             this.fireEvent('exception', this, xhr);
36952             return;
36953         }
36954         
36955         var response = Roo.decode(xhr.responseText);
36956         
36957         this.fireEvent('upload', this, response);
36958         
36959     },
36960     
36961     xhrOnError : function()
36962     {
36963         if(this.loadMask){
36964             this.maskEl.unmask();
36965         }
36966         
36967         Roo.log('xhr on error');
36968         
36969         var response = Roo.decode(xhr.responseText);
36970           
36971         Roo.log(response);
36972         
36973     },
36974     
36975     prepare : function(file)
36976     {   
36977         if(this.loadMask){
36978             this.maskEl.mask(this.loadingText);
36979         }
36980         
36981         this.file = false;
36982         this.exif = {};
36983         
36984         if(typeof(file) === 'string'){
36985             this.loadCanvas(file);
36986             return;
36987         }
36988         
36989         if(!file || !this.urlAPI){
36990             return;
36991         }
36992         
36993         this.file = file;
36994         this.cropType = file.type;
36995         
36996         var _this = this;
36997         
36998         if(this.fireEvent('prepare', this, this.file) != false){
36999             
37000             var reader = new FileReader();
37001             
37002             reader.onload = function (e) {
37003                 if (e.target.error) {
37004                     Roo.log(e.target.error);
37005                     return;
37006                 }
37007                 
37008                 var buffer = e.target.result,
37009                     dataView = new DataView(buffer),
37010                     offset = 2,
37011                     maxOffset = dataView.byteLength - 4,
37012                     markerBytes,
37013                     markerLength;
37014                 
37015                 if (dataView.getUint16(0) === 0xffd8) {
37016                     while (offset < maxOffset) {
37017                         markerBytes = dataView.getUint16(offset);
37018                         
37019                         if ((markerBytes >= 0xffe0 && markerBytes <= 0xffef) || markerBytes === 0xfffe) {
37020                             markerLength = dataView.getUint16(offset + 2) + 2;
37021                             if (offset + markerLength > dataView.byteLength) {
37022                                 Roo.log('Invalid meta data: Invalid segment size.');
37023                                 break;
37024                             }
37025                             
37026                             if(markerBytes == 0xffe1){
37027                                 _this.parseExifData(
37028                                     dataView,
37029                                     offset,
37030                                     markerLength
37031                                 );
37032                             }
37033                             
37034                             offset += markerLength;
37035                             
37036                             continue;
37037                         }
37038                         
37039                         break;
37040                     }
37041                     
37042                 }
37043                 
37044                 var url = _this.urlAPI.createObjectURL(_this.file);
37045                 
37046                 _this.loadCanvas(url);
37047                 
37048                 return;
37049             }
37050             
37051             reader.readAsArrayBuffer(this.file);
37052             
37053         }
37054         
37055     },
37056     
37057     parseExifData : function(dataView, offset, length)
37058     {
37059         var tiffOffset = offset + 10,
37060             littleEndian,
37061             dirOffset;
37062     
37063         if (dataView.getUint32(offset + 4) !== 0x45786966) {
37064             // No Exif data, might be XMP data instead
37065             return;
37066         }
37067         
37068         // Check for the ASCII code for "Exif" (0x45786966):
37069         if (dataView.getUint32(offset + 4) !== 0x45786966) {
37070             // No Exif data, might be XMP data instead
37071             return;
37072         }
37073         if (tiffOffset + 8 > dataView.byteLength) {
37074             Roo.log('Invalid Exif data: Invalid segment size.');
37075             return;
37076         }
37077         // Check for the two null bytes:
37078         if (dataView.getUint16(offset + 8) !== 0x0000) {
37079             Roo.log('Invalid Exif data: Missing byte alignment offset.');
37080             return;
37081         }
37082         // Check the byte alignment:
37083         switch (dataView.getUint16(tiffOffset)) {
37084         case 0x4949:
37085             littleEndian = true;
37086             break;
37087         case 0x4D4D:
37088             littleEndian = false;
37089             break;
37090         default:
37091             Roo.log('Invalid Exif data: Invalid byte alignment marker.');
37092             return;
37093         }
37094         // Check for the TIFF tag marker (0x002A):
37095         if (dataView.getUint16(tiffOffset + 2, littleEndian) !== 0x002A) {
37096             Roo.log('Invalid Exif data: Missing TIFF marker.');
37097             return;
37098         }
37099         // Retrieve the directory offset bytes, usually 0x00000008 or 8 decimal:
37100         dirOffset = dataView.getUint32(tiffOffset + 4, littleEndian);
37101         
37102         this.parseExifTags(
37103             dataView,
37104             tiffOffset,
37105             tiffOffset + dirOffset,
37106             littleEndian
37107         );
37108     },
37109     
37110     parseExifTags : function(dataView, tiffOffset, dirOffset, littleEndian)
37111     {
37112         var tagsNumber,
37113             dirEndOffset,
37114             i;
37115         if (dirOffset + 6 > dataView.byteLength) {
37116             Roo.log('Invalid Exif data: Invalid directory offset.');
37117             return;
37118         }
37119         tagsNumber = dataView.getUint16(dirOffset, littleEndian);
37120         dirEndOffset = dirOffset + 2 + 12 * tagsNumber;
37121         if (dirEndOffset + 4 > dataView.byteLength) {
37122             Roo.log('Invalid Exif data: Invalid directory size.');
37123             return;
37124         }
37125         for (i = 0; i < tagsNumber; i += 1) {
37126             this.parseExifTag(
37127                 dataView,
37128                 tiffOffset,
37129                 dirOffset + 2 + 12 * i, // tag offset
37130                 littleEndian
37131             );
37132         }
37133         // Return the offset to the next directory:
37134         return dataView.getUint32(dirEndOffset, littleEndian);
37135     },
37136     
37137     parseExifTag : function (dataView, tiffOffset, offset, littleEndian) 
37138     {
37139         var tag = dataView.getUint16(offset, littleEndian);
37140         
37141         this.exif[tag] = this.getExifValue(
37142             dataView,
37143             tiffOffset,
37144             offset,
37145             dataView.getUint16(offset + 2, littleEndian), // tag type
37146             dataView.getUint32(offset + 4, littleEndian), // tag length
37147             littleEndian
37148         );
37149     },
37150     
37151     getExifValue : function (dataView, tiffOffset, offset, type, length, littleEndian)
37152     {
37153         var tagType = Roo.bootstrap.UploadCropbox.exifTagTypes[type],
37154             tagSize,
37155             dataOffset,
37156             values,
37157             i,
37158             str,
37159             c;
37160     
37161         if (!tagType) {
37162             Roo.log('Invalid Exif data: Invalid tag type.');
37163             return;
37164         }
37165         
37166         tagSize = tagType.size * length;
37167         // Determine if the value is contained in the dataOffset bytes,
37168         // or if the value at the dataOffset is a pointer to the actual data:
37169         dataOffset = tagSize > 4 ?
37170                 tiffOffset + dataView.getUint32(offset + 8, littleEndian) : (offset + 8);
37171         if (dataOffset + tagSize > dataView.byteLength) {
37172             Roo.log('Invalid Exif data: Invalid data offset.');
37173             return;
37174         }
37175         if (length === 1) {
37176             return tagType.getValue(dataView, dataOffset, littleEndian);
37177         }
37178         values = [];
37179         for (i = 0; i < length; i += 1) {
37180             values[i] = tagType.getValue(dataView, dataOffset + i * tagType.size, littleEndian);
37181         }
37182         
37183         if (tagType.ascii) {
37184             str = '';
37185             // Concatenate the chars:
37186             for (i = 0; i < values.length; i += 1) {
37187                 c = values[i];
37188                 // Ignore the terminating NULL byte(s):
37189                 if (c === '\u0000') {
37190                     break;
37191                 }
37192                 str += c;
37193             }
37194             return str;
37195         }
37196         return values;
37197     }
37198     
37199 });
37200
37201 Roo.apply(Roo.bootstrap.UploadCropbox, {
37202     tags : {
37203         'Orientation': 0x0112
37204     },
37205     
37206     Orientation: {
37207             1: 0, //'top-left',
37208 //            2: 'top-right',
37209             3: 180, //'bottom-right',
37210 //            4: 'bottom-left',
37211 //            5: 'left-top',
37212             6: 90, //'right-top',
37213 //            7: 'right-bottom',
37214             8: 270 //'left-bottom'
37215     },
37216     
37217     exifTagTypes : {
37218         // byte, 8-bit unsigned int:
37219         1: {
37220             getValue: function (dataView, dataOffset) {
37221                 return dataView.getUint8(dataOffset);
37222             },
37223             size: 1
37224         },
37225         // ascii, 8-bit byte:
37226         2: {
37227             getValue: function (dataView, dataOffset) {
37228                 return String.fromCharCode(dataView.getUint8(dataOffset));
37229             },
37230             size: 1,
37231             ascii: true
37232         },
37233         // short, 16 bit int:
37234         3: {
37235             getValue: function (dataView, dataOffset, littleEndian) {
37236                 return dataView.getUint16(dataOffset, littleEndian);
37237             },
37238             size: 2
37239         },
37240         // long, 32 bit int:
37241         4: {
37242             getValue: function (dataView, dataOffset, littleEndian) {
37243                 return dataView.getUint32(dataOffset, littleEndian);
37244             },
37245             size: 4
37246         },
37247         // rational = two long values, first is numerator, second is denominator:
37248         5: {
37249             getValue: function (dataView, dataOffset, littleEndian) {
37250                 return dataView.getUint32(dataOffset, littleEndian) /
37251                     dataView.getUint32(dataOffset + 4, littleEndian);
37252             },
37253             size: 8
37254         },
37255         // slong, 32 bit signed int:
37256         9: {
37257             getValue: function (dataView, dataOffset, littleEndian) {
37258                 return dataView.getInt32(dataOffset, littleEndian);
37259             },
37260             size: 4
37261         },
37262         // srational, two slongs, first is numerator, second is denominator:
37263         10: {
37264             getValue: function (dataView, dataOffset, littleEndian) {
37265                 return dataView.getInt32(dataOffset, littleEndian) /
37266                     dataView.getInt32(dataOffset + 4, littleEndian);
37267             },
37268             size: 8
37269         }
37270     },
37271     
37272     footer : {
37273         STANDARD : [
37274             {
37275                 tag : 'div',
37276                 cls : 'btn-group roo-upload-cropbox-rotate-left',
37277                 action : 'rotate-left',
37278                 cn : [
37279                     {
37280                         tag : 'button',
37281                         cls : 'btn btn-default',
37282                         html : '<i class="fa fa-undo"></i>'
37283                     }
37284                 ]
37285             },
37286             {
37287                 tag : 'div',
37288                 cls : 'btn-group roo-upload-cropbox-picture',
37289                 action : 'picture',
37290                 cn : [
37291                     {
37292                         tag : 'button',
37293                         cls : 'btn btn-default',
37294                         html : '<i class="fa fa-picture-o"></i>'
37295                     }
37296                 ]
37297             },
37298             {
37299                 tag : 'div',
37300                 cls : 'btn-group roo-upload-cropbox-rotate-right',
37301                 action : 'rotate-right',
37302                 cn : [
37303                     {
37304                         tag : 'button',
37305                         cls : 'btn btn-default',
37306                         html : '<i class="fa fa-repeat"></i>'
37307                     }
37308                 ]
37309             }
37310         ],
37311         DOCUMENT : [
37312             {
37313                 tag : 'div',
37314                 cls : 'btn-group roo-upload-cropbox-rotate-left',
37315                 action : 'rotate-left',
37316                 cn : [
37317                     {
37318                         tag : 'button',
37319                         cls : 'btn btn-default',
37320                         html : '<i class="fa fa-undo"></i>'
37321                     }
37322                 ]
37323             },
37324             {
37325                 tag : 'div',
37326                 cls : 'btn-group roo-upload-cropbox-download',
37327                 action : 'download',
37328                 cn : [
37329                     {
37330                         tag : 'button',
37331                         cls : 'btn btn-default',
37332                         html : '<i class="fa fa-download"></i>'
37333                     }
37334                 ]
37335             },
37336             {
37337                 tag : 'div',
37338                 cls : 'btn-group roo-upload-cropbox-crop',
37339                 action : 'crop',
37340                 cn : [
37341                     {
37342                         tag : 'button',
37343                         cls : 'btn btn-default',
37344                         html : '<i class="fa fa-crop"></i>'
37345                     }
37346                 ]
37347             },
37348             {
37349                 tag : 'div',
37350                 cls : 'btn-group roo-upload-cropbox-trash',
37351                 action : 'trash',
37352                 cn : [
37353                     {
37354                         tag : 'button',
37355                         cls : 'btn btn-default',
37356                         html : '<i class="fa fa-trash"></i>'
37357                     }
37358                 ]
37359             },
37360             {
37361                 tag : 'div',
37362                 cls : 'btn-group roo-upload-cropbox-rotate-right',
37363                 action : 'rotate-right',
37364                 cn : [
37365                     {
37366                         tag : 'button',
37367                         cls : 'btn btn-default',
37368                         html : '<i class="fa fa-repeat"></i>'
37369                     }
37370                 ]
37371             }
37372         ],
37373         ROTATOR : [
37374             {
37375                 tag : 'div',
37376                 cls : 'btn-group roo-upload-cropbox-rotate-left',
37377                 action : 'rotate-left',
37378                 cn : [
37379                     {
37380                         tag : 'button',
37381                         cls : 'btn btn-default',
37382                         html : '<i class="fa fa-undo"></i>'
37383                     }
37384                 ]
37385             },
37386             {
37387                 tag : 'div',
37388                 cls : 'btn-group roo-upload-cropbox-rotate-right',
37389                 action : 'rotate-right',
37390                 cn : [
37391                     {
37392                         tag : 'button',
37393                         cls : 'btn btn-default',
37394                         html : '<i class="fa fa-repeat"></i>'
37395                     }
37396                 ]
37397             }
37398         ]
37399     }
37400 });
37401
37402 /*
37403 * Licence: LGPL
37404 */
37405
37406 /**
37407  * @class Roo.bootstrap.DocumentManager
37408  * @extends Roo.bootstrap.Component
37409  * Bootstrap DocumentManager class
37410  * @cfg {String} paramName default 'imageUpload'
37411  * @cfg {String} toolTipName default 'filename'
37412  * @cfg {String} method default POST
37413  * @cfg {String} url action url
37414  * @cfg {Number} boxes number of boxes, 0 is no limit.. default 0
37415  * @cfg {Boolean} multiple multiple upload default true
37416  * @cfg {Number} thumbSize default 300
37417  * @cfg {String} fieldLabel
37418  * @cfg {Number} labelWidth default 4
37419  * @cfg {String} labelAlign (left|top) default left
37420  * @cfg {Boolean} editable (true|false) allow edit when upload a image default true
37421 * @cfg {Number} labellg set the width of label (1-12)
37422  * @cfg {Number} labelmd set the width of label (1-12)
37423  * @cfg {Number} labelsm set the width of label (1-12)
37424  * @cfg {Number} labelxs set the width of label (1-12)
37425  * 
37426  * @constructor
37427  * Create a new DocumentManager
37428  * @param {Object} config The config object
37429  */
37430
37431 Roo.bootstrap.DocumentManager = function(config){
37432     Roo.bootstrap.DocumentManager.superclass.constructor.call(this, config);
37433     
37434     this.files = [];
37435     this.delegates = [];
37436     
37437     this.addEvents({
37438         /**
37439          * @event initial
37440          * Fire when initial the DocumentManager
37441          * @param {Roo.bootstrap.DocumentManager} this
37442          */
37443         "initial" : true,
37444         /**
37445          * @event inspect
37446          * inspect selected file
37447          * @param {Roo.bootstrap.DocumentManager} this
37448          * @param {File} file
37449          */
37450         "inspect" : true,
37451         /**
37452          * @event exception
37453          * Fire when xhr load exception
37454          * @param {Roo.bootstrap.DocumentManager} this
37455          * @param {XMLHttpRequest} xhr
37456          */
37457         "exception" : true,
37458         /**
37459          * @event afterupload
37460          * Fire when xhr load exception
37461          * @param {Roo.bootstrap.DocumentManager} this
37462          * @param {XMLHttpRequest} xhr
37463          */
37464         "afterupload" : true,
37465         /**
37466          * @event prepare
37467          * prepare the form data
37468          * @param {Roo.bootstrap.DocumentManager} this
37469          * @param {Object} formData
37470          */
37471         "prepare" : true,
37472         /**
37473          * @event remove
37474          * Fire when remove the file
37475          * @param {Roo.bootstrap.DocumentManager} this
37476          * @param {Object} file
37477          */
37478         "remove" : true,
37479         /**
37480          * @event refresh
37481          * Fire after refresh the file
37482          * @param {Roo.bootstrap.DocumentManager} this
37483          */
37484         "refresh" : true,
37485         /**
37486          * @event click
37487          * Fire after click the image
37488          * @param {Roo.bootstrap.DocumentManager} this
37489          * @param {Object} file
37490          */
37491         "click" : true,
37492         /**
37493          * @event edit
37494          * Fire when upload a image and editable set to true
37495          * @param {Roo.bootstrap.DocumentManager} this
37496          * @param {Object} file
37497          */
37498         "edit" : true,
37499         /**
37500          * @event beforeselectfile
37501          * Fire before select file
37502          * @param {Roo.bootstrap.DocumentManager} this
37503          */
37504         "beforeselectfile" : true,
37505         /**
37506          * @event process
37507          * Fire before process file
37508          * @param {Roo.bootstrap.DocumentManager} this
37509          * @param {Object} file
37510          */
37511         "process" : true,
37512         /**
37513          * @event previewrendered
37514          * Fire when preview rendered
37515          * @param {Roo.bootstrap.DocumentManager} this
37516          * @param {Object} file
37517          */
37518         "previewrendered" : true,
37519         /**
37520          */
37521         "previewResize" : true
37522         
37523     });
37524 };
37525
37526 Roo.extend(Roo.bootstrap.DocumentManager, Roo.bootstrap.Component,  {
37527     
37528     boxes : 0,
37529     inputName : '',
37530     thumbSize : 300,
37531     multiple : true,
37532     files : false,
37533     method : 'POST',
37534     url : '',
37535     paramName : 'imageUpload',
37536     toolTipName : 'filename',
37537     fieldLabel : '',
37538     labelWidth : 4,
37539     labelAlign : 'left',
37540     editable : true,
37541     delegates : false,
37542     xhr : false, 
37543     
37544     labellg : 0,
37545     labelmd : 0,
37546     labelsm : 0,
37547     labelxs : 0,
37548     
37549     getAutoCreate : function()
37550     {   
37551         var managerWidget = {
37552             tag : 'div',
37553             cls : 'roo-document-manager',
37554             cn : [
37555                 {
37556                     tag : 'input',
37557                     cls : 'roo-document-manager-selector',
37558                     type : 'file'
37559                 },
37560                 {
37561                     tag : 'div',
37562                     cls : 'roo-document-manager-uploader',
37563                     cn : [
37564                         {
37565                             tag : 'div',
37566                             cls : 'roo-document-manager-upload-btn',
37567                             html : '<i class="fa fa-plus"></i>'
37568                         }
37569                     ]
37570                     
37571                 }
37572             ]
37573         };
37574         
37575         var content = [
37576             {
37577                 tag : 'div',
37578                 cls : 'column col-md-12',
37579                 cn : managerWidget
37580             }
37581         ];
37582         
37583         if(this.fieldLabel.length){
37584             
37585             content = [
37586                 {
37587                     tag : 'div',
37588                     cls : 'column col-md-12',
37589                     html : this.fieldLabel
37590                 },
37591                 {
37592                     tag : 'div',
37593                     cls : 'column col-md-12',
37594                     cn : managerWidget
37595                 }
37596             ];
37597
37598             if(this.labelAlign == 'left'){
37599                 content = [
37600                     {
37601                         tag : 'div',
37602                         cls : 'column',
37603                         html : this.fieldLabel
37604                     },
37605                     {
37606                         tag : 'div',
37607                         cls : 'column',
37608                         cn : managerWidget
37609                     }
37610                 ];
37611                 
37612                 if(this.labelWidth > 12){
37613                     content[0].style = "width: " + this.labelWidth + 'px';
37614                 }
37615
37616                 if(this.labelWidth < 13 && this.labelmd == 0){
37617                     this.labelmd = this.labelWidth;
37618                 }
37619
37620                 if(this.labellg > 0){
37621                     content[0].cls += ' col-lg-' + this.labellg;
37622                     content[1].cls += ' col-lg-' + (12 - this.labellg);
37623                 }
37624
37625                 if(this.labelmd > 0){
37626                     content[0].cls += ' col-md-' + this.labelmd;
37627                     content[1].cls += ' col-md-' + (12 - this.labelmd);
37628                 }
37629
37630                 if(this.labelsm > 0){
37631                     content[0].cls += ' col-sm-' + this.labelsm;
37632                     content[1].cls += ' col-sm-' + (12 - this.labelsm);
37633                 }
37634
37635                 if(this.labelxs > 0){
37636                     content[0].cls += ' col-xs-' + this.labelxs;
37637                     content[1].cls += ' col-xs-' + (12 - this.labelxs);
37638                 }
37639                 
37640             }
37641         }
37642         
37643         var cfg = {
37644             tag : 'div',
37645             cls : 'row clearfix',
37646             cn : content
37647         };
37648         
37649         return cfg;
37650         
37651     },
37652     
37653     initEvents : function()
37654     {
37655         this.managerEl = this.el.select('.roo-document-manager', true).first();
37656         this.managerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
37657         
37658         this.selectorEl = this.el.select('.roo-document-manager-selector', true).first();
37659         this.selectorEl.hide();
37660         
37661         if(this.multiple){
37662             this.selectorEl.attr('multiple', 'multiple');
37663         }
37664         
37665         this.selectorEl.on('change', this.onFileSelected, this);
37666         
37667         this.uploader = this.el.select('.roo-document-manager-uploader', true).first();
37668         this.uploader.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
37669         
37670         this.uploader.on('click', this.onUploaderClick, this);
37671         
37672         this.renderProgressDialog();
37673         
37674         var _this = this;
37675         
37676         window.addEventListener("resize", function() { _this.refresh(); } );
37677         
37678         this.fireEvent('initial', this);
37679     },
37680     
37681     renderProgressDialog : function()
37682     {
37683         var _this = this;
37684         
37685         this.progressDialog = new Roo.bootstrap.Modal({
37686             cls : 'roo-document-manager-progress-dialog',
37687             allow_close : false,
37688             animate : false,
37689             title : '',
37690             buttons : [
37691                 {
37692                     name  :'cancel',
37693                     weight : 'danger',
37694                     html : 'Cancel'
37695                 }
37696             ], 
37697             listeners : { 
37698                 btnclick : function() {
37699                     _this.uploadCancel();
37700                     this.hide();
37701                 }
37702             }
37703         });
37704          
37705         this.progressDialog.render(Roo.get(document.body));
37706          
37707         this.progress = new Roo.bootstrap.Progress({
37708             cls : 'roo-document-manager-progress',
37709             active : true,
37710             striped : true
37711         });
37712         
37713         this.progress.render(this.progressDialog.getChildContainer());
37714         
37715         this.progressBar = new Roo.bootstrap.ProgressBar({
37716             cls : 'roo-document-manager-progress-bar',
37717             aria_valuenow : 0,
37718             aria_valuemin : 0,
37719             aria_valuemax : 12,
37720             panel : 'success'
37721         });
37722         
37723         this.progressBar.render(this.progress.getChildContainer());
37724     },
37725     
37726     onUploaderClick : function(e)
37727     {
37728         e.preventDefault();
37729      
37730         if(this.fireEvent('beforeselectfile', this) != false){
37731             this.selectorEl.dom.click();
37732         }
37733         
37734     },
37735     
37736     onFileSelected : function(e)
37737     {
37738         e.preventDefault();
37739         
37740         if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
37741             return;
37742         }
37743         
37744         Roo.each(this.selectorEl.dom.files, function(file){
37745             if(this.fireEvent('inspect', this, file) != false){
37746                 this.files.push(file);
37747             }
37748         }, this);
37749         
37750         this.queue();
37751         
37752     },
37753     
37754     queue : function()
37755     {
37756         this.selectorEl.dom.value = '';
37757         
37758         if(!this.files || !this.files.length){
37759             return;
37760         }
37761         
37762         if(this.boxes > 0 && this.files.length > this.boxes){
37763             this.files = this.files.slice(0, this.boxes);
37764         }
37765         
37766         this.uploader.show();
37767         
37768         if(this.boxes > 0 && this.files.length > this.boxes - 1){
37769             this.uploader.hide();
37770         }
37771         
37772         var _this = this;
37773         
37774         var files = [];
37775         
37776         var docs = [];
37777         
37778         Roo.each(this.files, function(file){
37779             
37780             if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
37781                 var f = this.renderPreview(file);
37782                 files.push(f);
37783                 return;
37784             }
37785             
37786             if(file.type.indexOf('image') != -1){
37787                 this.delegates.push(
37788                     (function(){
37789                         _this.process(file);
37790                     }).createDelegate(this)
37791                 );
37792         
37793                 return;
37794             }
37795             
37796             docs.push(
37797                 (function(){
37798                     _this.process(file);
37799                 }).createDelegate(this)
37800             );
37801             
37802         }, this);
37803         
37804         this.files = files;
37805         
37806         this.delegates = this.delegates.concat(docs);
37807         
37808         if(!this.delegates.length){
37809             this.refresh();
37810             return;
37811         }
37812         
37813         this.progressBar.aria_valuemax = this.delegates.length;
37814         
37815         this.arrange();
37816         
37817         return;
37818     },
37819     
37820     arrange : function()
37821     {
37822         if(!this.delegates.length){
37823             this.progressDialog.hide();
37824             this.refresh();
37825             return;
37826         }
37827         
37828         var delegate = this.delegates.shift();
37829         
37830         this.progressDialog.show();
37831         
37832         this.progressDialog.setTitle((this.progressBar.aria_valuemax - this.delegates.length) + ' / ' + this.progressBar.aria_valuemax);
37833         
37834         this.progressBar.update(this.progressBar.aria_valuemax - this.delegates.length);
37835         
37836         delegate();
37837     },
37838     
37839     refresh : function()
37840     {
37841         this.uploader.show();
37842         
37843         if(this.boxes > 0 && this.files.length > this.boxes - 1){
37844             this.uploader.hide();
37845         }
37846         
37847         Roo.isTouch ? this.closable(false) : this.closable(true);
37848         
37849         this.fireEvent('refresh', this);
37850     },
37851     
37852     onRemove : function(e, el, o)
37853     {
37854         e.preventDefault();
37855         
37856         this.fireEvent('remove', this, o);
37857         
37858     },
37859     
37860     remove : function(o)
37861     {
37862         var files = [];
37863         
37864         Roo.each(this.files, function(file){
37865             if(typeof(file.id) == 'undefined' || file.id * 1 < 1 || file.id != o.id){
37866                 files.push(file);
37867                 return;
37868             }
37869
37870             o.target.remove();
37871
37872         }, this);
37873         
37874         this.files = files;
37875         
37876         this.refresh();
37877     },
37878     
37879     clear : function()
37880     {
37881         Roo.each(this.files, function(file){
37882             if(!file.target){
37883                 return;
37884             }
37885             
37886             file.target.remove();
37887
37888         }, this);
37889         
37890         this.files = [];
37891         
37892         this.refresh();
37893     },
37894     
37895     onClick : function(e, el, o)
37896     {
37897         e.preventDefault();
37898         
37899         this.fireEvent('click', this, o);
37900         
37901     },
37902     
37903     closable : function(closable)
37904     {
37905         Roo.each(this.managerEl.select('.roo-document-manager-preview > button.close', true).elements, function(el){
37906             
37907             el.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
37908             
37909             if(closable){
37910                 el.show();
37911                 return;
37912             }
37913             
37914             el.hide();
37915             
37916         }, this);
37917     },
37918     
37919     xhrOnLoad : function(xhr)
37920     {
37921         Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
37922             el.remove();
37923         }, this);
37924         
37925         if (xhr.readyState !== 4) {
37926             this.arrange();
37927             this.fireEvent('exception', this, xhr);
37928             return;
37929         }
37930
37931         var response = Roo.decode(xhr.responseText);
37932         
37933         if(!response.success){
37934             this.arrange();
37935             this.fireEvent('exception', this, xhr);
37936             return;
37937         }
37938         
37939         var file = this.renderPreview(response.data);
37940         
37941         this.files.push(file);
37942         
37943         this.arrange();
37944         
37945         this.fireEvent('afterupload', this, xhr);
37946         
37947     },
37948     
37949     xhrOnError : function(xhr)
37950     {
37951         Roo.log('xhr on error');
37952         
37953         var response = Roo.decode(xhr.responseText);
37954           
37955         Roo.log(response);
37956         
37957         this.arrange();
37958     },
37959     
37960     process : function(file)
37961     {
37962         if(this.fireEvent('process', this, file) !== false){
37963             if(this.editable && file.type.indexOf('image') != -1){
37964                 this.fireEvent('edit', this, file);
37965                 return;
37966             }
37967
37968             this.uploadStart(file, false);
37969
37970             return;
37971         }
37972         
37973     },
37974     
37975     uploadStart : function(file, crop)
37976     {
37977         this.xhr = new XMLHttpRequest();
37978         
37979         if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
37980             this.arrange();
37981             return;
37982         }
37983         
37984         file.xhr = this.xhr;
37985             
37986         this.managerEl.createChild({
37987             tag : 'div',
37988             cls : 'roo-document-manager-loading',
37989             cn : [
37990                 {
37991                     tag : 'div',
37992                     tooltip : file.name,
37993                     cls : 'roo-document-manager-thumb',
37994                     html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
37995                 }
37996             ]
37997
37998         });
37999
38000         this.xhr.open(this.method, this.url, true);
38001         
38002         var headers = {
38003             "Accept": "application/json",
38004             "Cache-Control": "no-cache",
38005             "X-Requested-With": "XMLHttpRequest"
38006         };
38007         
38008         for (var headerName in headers) {
38009             var headerValue = headers[headerName];
38010             if (headerValue) {
38011                 this.xhr.setRequestHeader(headerName, headerValue);
38012             }
38013         }
38014         
38015         var _this = this;
38016         
38017         this.xhr.onload = function()
38018         {
38019             _this.xhrOnLoad(_this.xhr);
38020         }
38021         
38022         this.xhr.onerror = function()
38023         {
38024             _this.xhrOnError(_this.xhr);
38025         }
38026         
38027         var formData = new FormData();
38028
38029         formData.append('returnHTML', 'NO');
38030         
38031         if(crop){
38032             formData.append('crop', crop);
38033         }
38034         
38035         formData.append(this.paramName, file, file.name);
38036         
38037         var options = {
38038             file : file, 
38039             manually : false
38040         };
38041         
38042         if(this.fireEvent('prepare', this, formData, options) != false){
38043             
38044             if(options.manually){
38045                 return;
38046             }
38047             
38048             this.xhr.send(formData);
38049             return;
38050         };
38051         
38052         this.uploadCancel();
38053     },
38054     
38055     uploadCancel : function()
38056     {
38057         if (this.xhr) {
38058             this.xhr.abort();
38059         }
38060         
38061         this.delegates = [];
38062         
38063         Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
38064             el.remove();
38065         }, this);
38066         
38067         this.arrange();
38068     },
38069     
38070     renderPreview : function(file)
38071     {
38072         if(typeof(file.target) != 'undefined' && file.target){
38073             return file;
38074         }
38075         
38076         var img_src = encodeURI(baseURL +'/Images/Thumb/' + this.thumbSize + '/' + file.id + '/' + file.filename);
38077         
38078         var previewEl = this.managerEl.createChild({
38079             tag : 'div',
38080             cls : 'roo-document-manager-preview',
38081             cn : [
38082                 {
38083                     tag : 'div',
38084                     tooltip : file[this.toolTipName],
38085                     cls : 'roo-document-manager-thumb',
38086                     html : '<img tooltip="' + file[this.toolTipName] + '" src="' + img_src + '">'
38087                 },
38088                 {
38089                     tag : 'button',
38090                     cls : 'close',
38091                     html : '<i class="fa fa-times-circle"></i>'
38092                 }
38093             ]
38094         });
38095
38096         var close = previewEl.select('button.close', true).first();
38097
38098         close.on('click', this.onRemove, this, file);
38099
38100         file.target = previewEl;
38101
38102         var image = previewEl.select('img', true).first();
38103         
38104         var _this = this;
38105         
38106         image.dom.addEventListener("load", function(){ _this.onPreviewLoad(file, image); });
38107         
38108         image.on('click', this.onClick, this, file);
38109         
38110         this.fireEvent('previewrendered', this, file);
38111         
38112         return file;
38113         
38114     },
38115     
38116     onPreviewLoad : function(file, image)
38117     {
38118         if(typeof(file.target) == 'undefined' || !file.target){
38119             return;
38120         }
38121         
38122         var width = image.dom.naturalWidth || image.dom.width;
38123         var height = image.dom.naturalHeight || image.dom.height;
38124         
38125         if(!this.previewResize) {
38126             return;
38127         }
38128         
38129         if(width > height){
38130             file.target.addClass('wide');
38131             return;
38132         }
38133         
38134         file.target.addClass('tall');
38135         return;
38136         
38137     },
38138     
38139     uploadFromSource : function(file, crop)
38140     {
38141         this.xhr = new XMLHttpRequest();
38142         
38143         this.managerEl.createChild({
38144             tag : 'div',
38145             cls : 'roo-document-manager-loading',
38146             cn : [
38147                 {
38148                     tag : 'div',
38149                     tooltip : file.name,
38150                     cls : 'roo-document-manager-thumb',
38151                     html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
38152                 }
38153             ]
38154
38155         });
38156
38157         this.xhr.open(this.method, this.url, true);
38158         
38159         var headers = {
38160             "Accept": "application/json",
38161             "Cache-Control": "no-cache",
38162             "X-Requested-With": "XMLHttpRequest"
38163         };
38164         
38165         for (var headerName in headers) {
38166             var headerValue = headers[headerName];
38167             if (headerValue) {
38168                 this.xhr.setRequestHeader(headerName, headerValue);
38169             }
38170         }
38171         
38172         var _this = this;
38173         
38174         this.xhr.onload = function()
38175         {
38176             _this.xhrOnLoad(_this.xhr);
38177         }
38178         
38179         this.xhr.onerror = function()
38180         {
38181             _this.xhrOnError(_this.xhr);
38182         }
38183         
38184         var formData = new FormData();
38185
38186         formData.append('returnHTML', 'NO');
38187         
38188         formData.append('crop', crop);
38189         
38190         if(typeof(file.filename) != 'undefined'){
38191             formData.append('filename', file.filename);
38192         }
38193         
38194         if(typeof(file.mimetype) != 'undefined'){
38195             formData.append('mimetype', file.mimetype);
38196         }
38197         
38198         Roo.log(formData);
38199         
38200         if(this.fireEvent('prepare', this, formData) != false){
38201             this.xhr.send(formData);
38202         };
38203     }
38204 });
38205
38206 /*
38207 * Licence: LGPL
38208 */
38209
38210 /**
38211  * @class Roo.bootstrap.DocumentViewer
38212  * @extends Roo.bootstrap.Component
38213  * Bootstrap DocumentViewer class
38214  * @cfg {Boolean} showDownload (true|false) show download button (default true)
38215  * @cfg {Boolean} showTrash (true|false) show trash button (default true)
38216  * 
38217  * @constructor
38218  * Create a new DocumentViewer
38219  * @param {Object} config The config object
38220  */
38221
38222 Roo.bootstrap.DocumentViewer = function(config){
38223     Roo.bootstrap.DocumentViewer.superclass.constructor.call(this, config);
38224     
38225     this.addEvents({
38226         /**
38227          * @event initial
38228          * Fire after initEvent
38229          * @param {Roo.bootstrap.DocumentViewer} this
38230          */
38231         "initial" : true,
38232         /**
38233          * @event click
38234          * Fire after click
38235          * @param {Roo.bootstrap.DocumentViewer} this
38236          */
38237         "click" : true,
38238         /**
38239          * @event download
38240          * Fire after download button
38241          * @param {Roo.bootstrap.DocumentViewer} this
38242          */
38243         "download" : true,
38244         /**
38245          * @event trash
38246          * Fire after trash button
38247          * @param {Roo.bootstrap.DocumentViewer} this
38248          */
38249         "trash" : true
38250         
38251     });
38252 };
38253
38254 Roo.extend(Roo.bootstrap.DocumentViewer, Roo.bootstrap.Component,  {
38255     
38256     showDownload : true,
38257     
38258     showTrash : true,
38259     
38260     getAutoCreate : function()
38261     {
38262         var cfg = {
38263             tag : 'div',
38264             cls : 'roo-document-viewer',
38265             cn : [
38266                 {
38267                     tag : 'div',
38268                     cls : 'roo-document-viewer-body',
38269                     cn : [
38270                         {
38271                             tag : 'div',
38272                             cls : 'roo-document-viewer-thumb',
38273                             cn : [
38274                                 {
38275                                     tag : 'img',
38276                                     cls : 'roo-document-viewer-image'
38277                                 }
38278                             ]
38279                         }
38280                     ]
38281                 },
38282                 {
38283                     tag : 'div',
38284                     cls : 'roo-document-viewer-footer',
38285                     cn : {
38286                         tag : 'div',
38287                         cls : 'btn-group btn-group-justified roo-document-viewer-btn-group',
38288                         cn : [
38289                             {
38290                                 tag : 'div',
38291                                 cls : 'btn-group roo-document-viewer-download',
38292                                 cn : [
38293                                     {
38294                                         tag : 'button',
38295                                         cls : 'btn btn-default',
38296                                         html : '<i class="fa fa-download"></i>'
38297                                     }
38298                                 ]
38299                             },
38300                             {
38301                                 tag : 'div',
38302                                 cls : 'btn-group roo-document-viewer-trash',
38303                                 cn : [
38304                                     {
38305                                         tag : 'button',
38306                                         cls : 'btn btn-default',
38307                                         html : '<i class="fa fa-trash"></i>'
38308                                     }
38309                                 ]
38310                             }
38311                         ]
38312                     }
38313                 }
38314             ]
38315         };
38316         
38317         return cfg;
38318     },
38319     
38320     initEvents : function()
38321     {
38322         this.bodyEl = this.el.select('.roo-document-viewer-body', true).first();
38323         this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY);
38324         
38325         this.thumbEl = this.el.select('.roo-document-viewer-thumb', true).first();
38326         this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY);
38327         
38328         this.imageEl = this.el.select('.roo-document-viewer-image', true).first();
38329         this.imageEl.setVisibilityMode(Roo.Element.DISPLAY);
38330         
38331         this.footerEl = this.el.select('.roo-document-viewer-footer', true).first();
38332         this.footerEl.setVisibilityMode(Roo.Element.DISPLAY);
38333         
38334         this.downloadBtn = this.el.select('.roo-document-viewer-download', true).first();
38335         this.downloadBtn.setVisibilityMode(Roo.Element.DISPLAY);
38336         
38337         this.trashBtn = this.el.select('.roo-document-viewer-trash', true).first();
38338         this.trashBtn.setVisibilityMode(Roo.Element.DISPLAY);
38339         
38340         this.bodyEl.on('click', this.onClick, this);
38341         this.downloadBtn.on('click', this.onDownload, this);
38342         this.trashBtn.on('click', this.onTrash, this);
38343         
38344         this.downloadBtn.hide();
38345         this.trashBtn.hide();
38346         
38347         if(this.showDownload){
38348             this.downloadBtn.show();
38349         }
38350         
38351         if(this.showTrash){
38352             this.trashBtn.show();
38353         }
38354         
38355         if(!this.showDownload && !this.showTrash) {
38356             this.footerEl.hide();
38357         }
38358         
38359     },
38360     
38361     initial : function()
38362     {
38363         this.fireEvent('initial', this);
38364         
38365     },
38366     
38367     onClick : function(e)
38368     {
38369         e.preventDefault();
38370         
38371         this.fireEvent('click', this);
38372     },
38373     
38374     onDownload : function(e)
38375     {
38376         e.preventDefault();
38377         
38378         this.fireEvent('download', this);
38379     },
38380     
38381     onTrash : function(e)
38382     {
38383         e.preventDefault();
38384         
38385         this.fireEvent('trash', this);
38386     }
38387     
38388 });
38389 /*
38390  * - LGPL
38391  *
38392  * FieldLabel
38393  * 
38394  */
38395
38396 /**
38397  * @class Roo.bootstrap.form.FieldLabel
38398  * @extends Roo.bootstrap.Component
38399  * Bootstrap FieldLabel class
38400  * @cfg {String} html contents of the element
38401  * @cfg {String} tag tag of the element default label
38402  * @cfg {String} cls class of the element
38403  * @cfg {String} target label target 
38404  * @cfg {Boolean} allowBlank (true|false) target allowBlank default true
38405  * @cfg {String} invalidClass DEPRICATED - BS4 uses is-invalid
38406  * @cfg {String} validClass DEPRICATED - BS4 uses is-valid
38407  * @cfg {String} iconTooltip default "This field is required"
38408  * @cfg {String} indicatorpos (left|right) default left
38409  * 
38410  * @constructor
38411  * Create a new FieldLabel
38412  * @param {Object} config The config object
38413  */
38414
38415 Roo.bootstrap.form.FieldLabel = function(config){
38416     Roo.bootstrap.Element.superclass.constructor.call(this, config);
38417     
38418     this.addEvents({
38419             /**
38420              * @event invalid
38421              * Fires after the field has been marked as invalid.
38422              * @param {Roo.form.FieldLabel} this
38423              * @param {String} msg The validation message
38424              */
38425             invalid : true,
38426             /**
38427              * @event valid
38428              * Fires after the field has been validated with no errors.
38429              * @param {Roo.form.FieldLabel} this
38430              */
38431             valid : true
38432         });
38433 };
38434
38435 Roo.extend(Roo.bootstrap.form.FieldLabel, Roo.bootstrap.Component,  {
38436     
38437     tag: 'label',
38438     cls: '',
38439     html: '',
38440     target: '',
38441     allowBlank : true,
38442     invalidClass : 'has-warning',
38443     validClass : 'has-success',
38444     iconTooltip : 'This field is required',
38445     indicatorpos : 'left',
38446     
38447     getAutoCreate : function(){
38448         
38449         var cls = "";
38450         if (!this.allowBlank) {
38451             cls  = "visible";
38452         }
38453         
38454         var cfg = {
38455             tag : this.tag,
38456             cls : 'roo-bootstrap-field-label ' + this.cls,
38457             for : this.target,
38458             cn : [
38459                 {
38460                     tag : 'i',
38461                     cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star ' + cls,
38462                     tooltip : this.iconTooltip
38463                 },
38464                 {
38465                     tag : 'span',
38466                     html : this.html
38467                 }
38468             ] 
38469         };
38470         
38471         if(this.indicatorpos == 'right'){
38472             var cfg = {
38473                 tag : this.tag,
38474                 cls : 'roo-bootstrap-field-label ' + this.cls,
38475                 for : this.target,
38476                 cn : [
38477                     {
38478                         tag : 'span',
38479                         html : this.html
38480                     },
38481                     {
38482                         tag : 'i',
38483                         cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star '+ cls,
38484                         tooltip : this.iconTooltip
38485                     }
38486                 ] 
38487             };
38488         }
38489         
38490         return cfg;
38491     },
38492     
38493     initEvents: function() 
38494     {
38495         Roo.bootstrap.Element.superclass.initEvents.call(this);
38496         
38497         this.indicator = this.indicatorEl();
38498         
38499         if(this.indicator){
38500             this.indicator.removeClass('visible');
38501             this.indicator.addClass('invisible');
38502         }
38503         
38504         Roo.bootstrap.form.FieldLabel.register(this);
38505     },
38506     
38507     indicatorEl : function()
38508     {
38509         var indicator = this.el.select('i.roo-required-indicator',true).first();
38510         
38511         if(!indicator){
38512             return false;
38513         }
38514         
38515         return indicator;
38516         
38517     },
38518     
38519     /**
38520      * Mark this field as valid
38521      */
38522     markValid : function()
38523     {
38524         if(this.indicator){
38525             this.indicator.removeClass('visible');
38526             this.indicator.addClass('invisible');
38527         }
38528         if (Roo.bootstrap.version == 3) {
38529             this.el.removeClass(this.invalidClass);
38530             this.el.addClass(this.validClass);
38531         } else {
38532             this.el.removeClass('is-invalid');
38533             this.el.addClass('is-valid');
38534         }
38535         
38536         
38537         this.fireEvent('valid', this);
38538     },
38539     
38540     /**
38541      * Mark this field as invalid
38542      * @param {String} msg The validation message
38543      */
38544     markInvalid : function(msg)
38545     {
38546         if(this.indicator){
38547             this.indicator.removeClass('invisible');
38548             this.indicator.addClass('visible');
38549         }
38550           if (Roo.bootstrap.version == 3) {
38551             this.el.removeClass(this.validClass);
38552             this.el.addClass(this.invalidClass);
38553         } else {
38554             this.el.removeClass('is-valid');
38555             this.el.addClass('is-invalid');
38556         }
38557         
38558         
38559         this.fireEvent('invalid', this, msg);
38560     }
38561     
38562    
38563 });
38564
38565 Roo.apply(Roo.bootstrap.form.FieldLabel, {
38566     
38567     groups: {},
38568     
38569      /**
38570     * register a FieldLabel Group
38571     * @param {Roo.bootstrap.form.FieldLabel} the FieldLabel to add
38572     */
38573     register : function(label)
38574     {
38575         if(this.groups.hasOwnProperty(label.target)){
38576             return;
38577         }
38578      
38579         this.groups[label.target] = label;
38580         
38581     },
38582     /**
38583     * fetch a FieldLabel Group based on the target
38584     * @param {string} target
38585     * @returns {Roo.bootstrap.form.FieldLabel} the CheckBox group
38586     */
38587     get: function(target) {
38588         if (typeof(this.groups[target]) == 'undefined') {
38589             return false;
38590         }
38591         
38592         return this.groups[target] ;
38593     }
38594 });
38595
38596  
38597
38598  /*
38599  * - LGPL
38600  *
38601  * page DateSplitField.
38602  * 
38603  */
38604
38605
38606 /**
38607  * @class Roo.bootstrap.form.DateSplitField
38608  * @extends Roo.bootstrap.Component
38609  * Bootstrap DateSplitField class
38610  * @cfg {string} fieldLabel - the label associated
38611  * @cfg {Number} labelWidth set the width of label (0-12)
38612  * @cfg {String} labelAlign (top|left)
38613  * @cfg {Boolean} dayAllowBlank (true|false) default false
38614  * @cfg {Boolean} monthAllowBlank (true|false) default false
38615  * @cfg {Boolean} yearAllowBlank (true|false) default false
38616  * @cfg {string} dayPlaceholder 
38617  * @cfg {string} monthPlaceholder
38618  * @cfg {string} yearPlaceholder
38619  * @cfg {string} dayFormat default 'd'
38620  * @cfg {string} monthFormat default 'm'
38621  * @cfg {string} yearFormat default 'Y'
38622  * @cfg {Number} labellg set the width of label (1-12)
38623  * @cfg {Number} labelmd set the width of label (1-12)
38624  * @cfg {Number} labelsm set the width of label (1-12)
38625  * @cfg {Number} labelxs set the width of label (1-12)
38626
38627  *     
38628  * @constructor
38629  * Create a new DateSplitField
38630  * @param {Object} config The config object
38631  */
38632
38633 Roo.bootstrap.form.DateSplitField = function(config){
38634     Roo.bootstrap.form.DateSplitField.superclass.constructor.call(this, config);
38635     
38636     this.addEvents({
38637         // raw events
38638          /**
38639          * @event years
38640          * getting the data of years
38641          * @param {Roo.bootstrap.form.DateSplitField} this
38642          * @param {Object} years
38643          */
38644         "years" : true,
38645         /**
38646          * @event days
38647          * getting the data of days
38648          * @param {Roo.bootstrap.form.DateSplitField} this
38649          * @param {Object} days
38650          */
38651         "days" : true,
38652         /**
38653          * @event invalid
38654          * Fires after the field has been marked as invalid.
38655          * @param {Roo.form.Field} this
38656          * @param {String} msg The validation message
38657          */
38658         invalid : true,
38659        /**
38660          * @event valid
38661          * Fires after the field has been validated with no errors.
38662          * @param {Roo.form.Field} this
38663          */
38664         valid : true
38665     });
38666 };
38667
38668 Roo.extend(Roo.bootstrap.form.DateSplitField, Roo.bootstrap.Component,  {
38669     
38670     fieldLabel : '',
38671     labelAlign : 'top',
38672     labelWidth : 3,
38673     dayAllowBlank : false,
38674     monthAllowBlank : false,
38675     yearAllowBlank : false,
38676     dayPlaceholder : '',
38677     monthPlaceholder : '',
38678     yearPlaceholder : '',
38679     dayFormat : 'd',
38680     monthFormat : 'm',
38681     yearFormat : 'Y',
38682     isFormField : true,
38683     labellg : 0,
38684     labelmd : 0,
38685     labelsm : 0,
38686     labelxs : 0,
38687     
38688     getAutoCreate : function()
38689     {
38690         var cfg = {
38691             tag : 'div',
38692             cls : 'row roo-date-split-field-group',
38693             cn : [
38694                 {
38695                     tag : 'input',
38696                     type : 'hidden',
38697                     cls : 'form-hidden-field roo-date-split-field-group-value',
38698                     name : this.name
38699                 }
38700             ]
38701         };
38702         
38703         var labelCls = 'col-md-12';
38704         var contentCls = 'col-md-4';
38705         
38706         if(this.fieldLabel){
38707             
38708             var label = {
38709                 tag : 'div',
38710                 cls : 'column roo-date-split-field-label col-md-' + ((this.labelAlign == 'top') ? '12' : this.labelWidth),
38711                 cn : [
38712                     {
38713                         tag : 'label',
38714                         html : this.fieldLabel
38715                     }
38716                 ]
38717             };
38718             
38719             if(this.labelAlign == 'left'){
38720             
38721                 if(this.labelWidth > 12){
38722                     label.style = "width: " + this.labelWidth + 'px';
38723                 }
38724
38725                 if(this.labelWidth < 13 && this.labelmd == 0){
38726                     this.labelmd = this.labelWidth;
38727                 }
38728
38729                 if(this.labellg > 0){
38730                     labelCls = ' col-lg-' + this.labellg;
38731                     contentCls = ' col-lg-' + ((12 - this.labellg) / 3);
38732                 }
38733
38734                 if(this.labelmd > 0){
38735                     labelCls = ' col-md-' + this.labelmd;
38736                     contentCls = ' col-md-' + ((12 - this.labelmd) / 3);
38737                 }
38738
38739                 if(this.labelsm > 0){
38740                     labelCls = ' col-sm-' + this.labelsm;
38741                     contentCls = ' col-sm-' + ((12 - this.labelsm) / 3);
38742                 }
38743
38744                 if(this.labelxs > 0){
38745                     labelCls = ' col-xs-' + this.labelxs;
38746                     contentCls = ' col-xs-' + ((12 - this.labelxs) / 3);
38747                 }
38748             }
38749             
38750             label.cls += ' ' + labelCls;
38751             
38752             cfg.cn.push(label);
38753         }
38754         
38755         Roo.each(['day', 'month', 'year'], function(t){
38756             cfg.cn.push({
38757                 tag : 'div',
38758                 cls : 'column roo-date-split-field-' + t + ' ' + contentCls
38759             });
38760         }, this);
38761         
38762         return cfg;
38763     },
38764     
38765     inputEl: function ()
38766     {
38767         return this.el.select('.roo-date-split-field-group-value', true).first();
38768     },
38769     
38770     onRender : function(ct, position) 
38771     {
38772         var _this = this;
38773         
38774         Roo.bootstrap.DateSplitFiel.superclass.onRender.call(this, ct, position);
38775         
38776         this.inputEl = this.el.select('.roo-date-split-field-group-value', true).first();
38777         
38778         this.dayField = new Roo.bootstrap.form.ComboBox({
38779             allowBlank : this.dayAllowBlank,
38780             alwaysQuery : true,
38781             displayField : 'value',
38782             editable : false,
38783             fieldLabel : '',
38784             forceSelection : true,
38785             mode : 'local',
38786             placeholder : this.dayPlaceholder,
38787             selectOnFocus : true,
38788             tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
38789             triggerAction : 'all',
38790             typeAhead : true,
38791             valueField : 'value',
38792             store : new Roo.data.SimpleStore({
38793                 data : (function() {    
38794                     var days = [];
38795                     _this.fireEvent('days', _this, days);
38796                     return days;
38797                 })(),
38798                 fields : [ 'value' ]
38799             }),
38800             listeners : {
38801                 select : function (_self, record, index)
38802                 {
38803                     _this.setValue(_this.getValue());
38804                 }
38805             }
38806         });
38807
38808         this.dayField.render(this.el.select('.roo-date-split-field-day', true).first(), null);
38809         
38810         this.monthField = new Roo.bootstrap.form.MonthField({
38811             after : '<i class=\"fa fa-calendar\"></i>',
38812             allowBlank : this.monthAllowBlank,
38813             placeholder : this.monthPlaceholder,
38814             readOnly : true,
38815             listeners : {
38816                 render : function (_self)
38817                 {
38818                     this.el.select('span.input-group-addon', true).first().on('click', function(e){
38819                         e.preventDefault();
38820                         _self.focus();
38821                     });
38822                 },
38823                 select : function (_self, oldvalue, newvalue)
38824                 {
38825                     _this.setValue(_this.getValue());
38826                 }
38827             }
38828         });
38829         
38830         this.monthField.render(this.el.select('.roo-date-split-field-month', true).first(), null);
38831         
38832         this.yearField = new Roo.bootstrap.form.ComboBox({
38833             allowBlank : this.yearAllowBlank,
38834             alwaysQuery : true,
38835             displayField : 'value',
38836             editable : false,
38837             fieldLabel : '',
38838             forceSelection : true,
38839             mode : 'local',
38840             placeholder : this.yearPlaceholder,
38841             selectOnFocus : true,
38842             tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
38843             triggerAction : 'all',
38844             typeAhead : true,
38845             valueField : 'value',
38846             store : new Roo.data.SimpleStore({
38847                 data : (function() {
38848                     var years = [];
38849                     _this.fireEvent('years', _this, years);
38850                     return years;
38851                 })(),
38852                 fields : [ 'value' ]
38853             }),
38854             listeners : {
38855                 select : function (_self, record, index)
38856                 {
38857                     _this.setValue(_this.getValue());
38858                 }
38859             }
38860         });
38861
38862         this.yearField.render(this.el.select('.roo-date-split-field-year', true).first(), null);
38863     },
38864     
38865     setValue : function(v, format)
38866     {
38867         this.inputEl.dom.value = v;
38868         
38869         var f = format || (this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat);
38870         
38871         var d = Date.parseDate(v, f);
38872         
38873         if(!d){
38874             this.validate();
38875             return;
38876         }
38877         
38878         this.setDay(d.format(this.dayFormat));
38879         this.setMonth(d.format(this.monthFormat));
38880         this.setYear(d.format(this.yearFormat));
38881         
38882         this.validate();
38883         
38884         return;
38885     },
38886     
38887     setDay : function(v)
38888     {
38889         this.dayField.setValue(v);
38890         this.inputEl.dom.value = this.getValue();
38891         this.validate();
38892         return;
38893     },
38894     
38895     setMonth : function(v)
38896     {
38897         this.monthField.setValue(v, true);
38898         this.inputEl.dom.value = this.getValue();
38899         this.validate();
38900         return;
38901     },
38902     
38903     setYear : function(v)
38904     {
38905         this.yearField.setValue(v);
38906         this.inputEl.dom.value = this.getValue();
38907         this.validate();
38908         return;
38909     },
38910     
38911     getDay : function()
38912     {
38913         return this.dayField.getValue();
38914     },
38915     
38916     getMonth : function()
38917     {
38918         return this.monthField.getValue();
38919     },
38920     
38921     getYear : function()
38922     {
38923         return this.yearField.getValue();
38924     },
38925     
38926     getValue : function()
38927     {
38928         var f = this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat;
38929         
38930         var date = this.yearField.getValue() + '-' + this.monthField.getValue() + '-' + this.dayField.getValue();
38931         
38932         return date;
38933     },
38934     
38935     reset : function()
38936     {
38937         this.setDay('');
38938         this.setMonth('');
38939         this.setYear('');
38940         this.inputEl.dom.value = '';
38941         this.validate();
38942         return;
38943     },
38944     
38945     validate : function()
38946     {
38947         var d = this.dayField.validate();
38948         var m = this.monthField.validate();
38949         var y = this.yearField.validate();
38950         
38951         var valid = true;
38952         
38953         if(
38954                 (!this.dayAllowBlank && !d) ||
38955                 (!this.monthAllowBlank && !m) ||
38956                 (!this.yearAllowBlank && !y)
38957         ){
38958             valid = false;
38959         }
38960         
38961         if(this.dayAllowBlank && this.monthAllowBlank && this.yearAllowBlank){
38962             return valid;
38963         }
38964         
38965         if(valid){
38966             this.markValid();
38967             return valid;
38968         }
38969         
38970         this.markInvalid();
38971         
38972         return valid;
38973     },
38974     
38975     markValid : function()
38976     {
38977         
38978         var label = this.el.select('label', true).first();
38979         var icon = this.el.select('i.fa-star', true).first();
38980
38981         if(label && icon){
38982             icon.remove();
38983         }
38984         
38985         this.fireEvent('valid', this);
38986     },
38987     
38988      /**
38989      * Mark this field as invalid
38990      * @param {String} msg The validation message
38991      */
38992     markInvalid : function(msg)
38993     {
38994         
38995         var label = this.el.select('label', true).first();
38996         var icon = this.el.select('i.fa-star', true).first();
38997
38998         if(label && !icon){
38999             this.el.select('.roo-date-split-field-label', true).createChild({
39000                 tag : 'i',
39001                 cls : 'text-danger fa fa-lg fa-star',
39002                 tooltip : 'This field is required',
39003                 style : 'margin-right:5px;'
39004             }, label, true);
39005         }
39006         
39007         this.fireEvent('invalid', this, msg);
39008     },
39009     
39010     clearInvalid : function()
39011     {
39012         var label = this.el.select('label', true).first();
39013         var icon = this.el.select('i.fa-star', true).first();
39014
39015         if(label && icon){
39016             icon.remove();
39017         }
39018         
39019         this.fireEvent('valid', this);
39020     },
39021     
39022     getName: function()
39023     {
39024         return this.name;
39025     }
39026     
39027 });
39028
39029  
39030
39031 /**
39032  * @class Roo.bootstrap.LayoutMasonry
39033  * @extends Roo.bootstrap.Component
39034  * @children Roo.bootstrap.Element Roo.bootstrap.Img Roo.bootstrap.MasonryBrick
39035  * Bootstrap Layout Masonry class
39036  *
39037  * This is based on 
39038  * http://masonry.desandro.com
39039  *
39040  * The idea is to render all the bricks based on vertical width...
39041  *
39042  * The original code extends 'outlayer' - we might need to use that....
39043
39044  * @constructor
39045  * Create a new Element
39046  * @param {Object} config The config object
39047  */
39048
39049 Roo.bootstrap.LayoutMasonry = function(config){
39050     
39051     Roo.bootstrap.LayoutMasonry.superclass.constructor.call(this, config);
39052     
39053     this.bricks = [];
39054     
39055     Roo.bootstrap.LayoutMasonry.register(this);
39056     
39057     this.addEvents({
39058         // raw events
39059         /**
39060          * @event layout
39061          * Fire after layout the items
39062          * @param {Roo.bootstrap.LayoutMasonry} this
39063          * @param {Roo.EventObject} e
39064          */
39065         "layout" : true
39066     });
39067     
39068 };
39069
39070 Roo.extend(Roo.bootstrap.LayoutMasonry, Roo.bootstrap.Component,  {
39071     
39072     /**
39073      * @cfg {Boolean} isLayoutInstant = no animation?
39074      */   
39075     isLayoutInstant : false, // needed?
39076    
39077     /**
39078      * @cfg {Number} boxWidth  width of the columns
39079      */   
39080     boxWidth : 450,
39081     
39082       /**
39083      * @cfg {Number} boxHeight  - 0 for square, or fix it at a certian height
39084      */   
39085     boxHeight : 0,
39086     
39087     /**
39088      * @cfg {Number} padWidth padding below box..
39089      */   
39090     padWidth : 10, 
39091     
39092     /**
39093      * @cfg {Number} gutter gutter width..
39094      */   
39095     gutter : 10,
39096     
39097      /**
39098      * @cfg {Number} maxCols maximum number of columns
39099      */   
39100     
39101     maxCols: 0,
39102     
39103     /**
39104      * @cfg {Boolean} isAutoInitial defalut true
39105      */   
39106     isAutoInitial : true, 
39107     
39108     containerWidth: 0,
39109     
39110     /**
39111      * @cfg {Boolean} isHorizontal defalut false
39112      */   
39113     isHorizontal : false, 
39114
39115     currentSize : null,
39116     
39117     tag: 'div',
39118     
39119     cls: '',
39120     
39121     bricks: null, //CompositeElement
39122     
39123     cols : 1,
39124     
39125     _isLayoutInited : false,
39126     
39127 //    isAlternative : false, // only use for vertical layout...
39128     
39129     /**
39130      * @cfg {Number} alternativePadWidth padding below box..
39131      */   
39132     alternativePadWidth : 50,
39133     
39134     selectedBrick : [],
39135     
39136     getAutoCreate : function(){
39137         
39138         var cfg = Roo.apply({}, Roo.bootstrap.LayoutMasonry.superclass.getAutoCreate.call(this));
39139         
39140         var cfg = {
39141             tag: this.tag,
39142             cls: 'blog-masonary-wrapper ' + this.cls,
39143             cn : {
39144                 cls : 'mas-boxes masonary'
39145             }
39146         };
39147         
39148         return cfg;
39149     },
39150     
39151     getChildContainer: function( )
39152     {
39153         if (this.boxesEl) {
39154             return this.boxesEl;
39155         }
39156         
39157         this.boxesEl = this.el.select('.mas-boxes').first();
39158         
39159         return this.boxesEl;
39160     },
39161     
39162     
39163     initEvents : function()
39164     {
39165         var _this = this;
39166         
39167         if(this.isAutoInitial){
39168             Roo.log('hook children rendered');
39169             this.on('childrenrendered', function() {
39170                 Roo.log('children rendered');
39171                 _this.initial();
39172             } ,this);
39173         }
39174     },
39175     
39176     initial : function()
39177     {
39178         this.selectedBrick = [];
39179         
39180         this.currentSize = this.el.getBox(true);
39181         
39182         Roo.EventManager.onWindowResize(this.resize, this); 
39183
39184         if(!this.isAutoInitial){
39185             this.layout();
39186             return;
39187         }
39188         
39189         this.layout();
39190         
39191         return;
39192         //this.layout.defer(500,this);
39193         
39194     },
39195     
39196     resize : function()
39197     {
39198         var cs = this.el.getBox(true);
39199         
39200         if (
39201                 this.currentSize.width == cs.width && 
39202                 this.currentSize.x == cs.x && 
39203                 this.currentSize.height == cs.height && 
39204                 this.currentSize.y == cs.y 
39205         ) {
39206             Roo.log("no change in with or X or Y");
39207             return;
39208         }
39209         
39210         this.currentSize = cs;
39211         
39212         this.layout();
39213         
39214     },
39215     
39216     layout : function()
39217     {   
39218         this._resetLayout();
39219         
39220         var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
39221         
39222         this.layoutItems( isInstant );
39223       
39224         this._isLayoutInited = true;
39225         
39226         this.fireEvent('layout', this);
39227         
39228     },
39229     
39230     _resetLayout : function()
39231     {
39232         if(this.isHorizontal){
39233             this.horizontalMeasureColumns();
39234             return;
39235         }
39236         
39237         this.verticalMeasureColumns();
39238         
39239     },
39240     
39241     verticalMeasureColumns : function()
39242     {
39243         this.getContainerWidth();
39244         
39245 //        if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
39246 //            this.colWidth = Math.floor(this.containerWidth * 0.8);
39247 //            return;
39248 //        }
39249         
39250         var boxWidth = this.boxWidth + this.padWidth;
39251         
39252         if(this.containerWidth < this.boxWidth){
39253             boxWidth = this.containerWidth
39254         }
39255         
39256         var containerWidth = this.containerWidth;
39257         
39258         var cols = Math.floor(containerWidth / boxWidth);
39259         
39260         this.cols = Math.max( cols, 1 );
39261         
39262         this.cols = this.maxCols > 0 ? Math.min( this.cols, this.maxCols ) : this.cols;
39263         
39264         var totalBoxWidth = this.cols * boxWidth - this.padWidth;
39265         
39266         var avail = Math.floor((containerWidth - totalBoxWidth) / this.cols);
39267         
39268         this.colWidth = boxWidth + avail - this.padWidth;
39269         
39270         this.unitWidth = Math.round((this.colWidth - (this.gutter * 2)) / 3);
39271         this.unitHeight = this.boxHeight > 0 ? this.boxHeight  : this.unitWidth;
39272     },
39273     
39274     horizontalMeasureColumns : function()
39275     {
39276         this.getContainerWidth();
39277         
39278         var boxWidth = this.boxWidth;
39279         
39280         if(this.containerWidth < boxWidth){
39281             boxWidth = this.containerWidth;
39282         }
39283         
39284         this.unitWidth = Math.floor((boxWidth - (this.gutter * 2)) / 3);
39285         
39286         this.el.setHeight(boxWidth);
39287         
39288     },
39289     
39290     getContainerWidth : function()
39291     {
39292         this.containerWidth = this.el.getBox(true).width;  //maybe use getComputedWidth
39293     },
39294     
39295     layoutItems : function( isInstant )
39296     {
39297         Roo.log(this.bricks);
39298         
39299         var items = Roo.apply([], this.bricks);
39300         
39301         if(this.isHorizontal){
39302             this._horizontalLayoutItems( items , isInstant );
39303             return;
39304         }
39305         
39306 //        if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
39307 //            this._verticalAlternativeLayoutItems( items , isInstant );
39308 //            return;
39309 //        }
39310         
39311         this._verticalLayoutItems( items , isInstant );
39312         
39313     },
39314     
39315     _verticalLayoutItems : function ( items , isInstant)
39316     {
39317         if ( !items || !items.length ) {
39318             return;
39319         }
39320         
39321         var standard = [
39322             ['xs', 'xs', 'xs', 'tall'],
39323             ['xs', 'xs', 'tall'],
39324             ['xs', 'xs', 'sm'],
39325             ['xs', 'xs', 'xs'],
39326             ['xs', 'tall'],
39327             ['xs', 'sm'],
39328             ['xs', 'xs'],
39329             ['xs'],
39330             
39331             ['sm', 'xs', 'xs'],
39332             ['sm', 'xs'],
39333             ['sm'],
39334             
39335             ['tall', 'xs', 'xs', 'xs'],
39336             ['tall', 'xs', 'xs'],
39337             ['tall', 'xs'],
39338             ['tall']
39339             
39340         ];
39341         
39342         var queue = [];
39343         
39344         var boxes = [];
39345         
39346         var box = [];
39347         
39348         Roo.each(items, function(item, k){
39349             
39350             switch (item.size) {
39351                 // these layouts take up a full box,
39352                 case 'md' :
39353                 case 'md-left' :
39354                 case 'md-right' :
39355                 case 'wide' :
39356                     
39357                     if(box.length){
39358                         boxes.push(box);
39359                         box = [];
39360                     }
39361                     
39362                     boxes.push([item]);
39363                     
39364                     break;
39365                     
39366                 case 'xs' :
39367                 case 'sm' :
39368                 case 'tall' :
39369                     
39370                     box.push(item);
39371                     
39372                     break;
39373                 default :
39374                     break;
39375                     
39376             }
39377             
39378         }, this);
39379         
39380         if(box.length){
39381             boxes.push(box);
39382             box = [];
39383         }
39384         
39385         var filterPattern = function(box, length)
39386         {
39387             if(!box.length){
39388                 return;
39389             }
39390             
39391             var match = false;
39392             
39393             var pattern = box.slice(0, length);
39394             
39395             var format = [];
39396             
39397             Roo.each(pattern, function(i){
39398                 format.push(i.size);
39399             }, this);
39400             
39401             Roo.each(standard, function(s){
39402                 
39403                 if(String(s) != String(format)){
39404                     return;
39405                 }
39406                 
39407                 match = true;
39408                 return false;
39409                 
39410             }, this);
39411             
39412             if(!match && length == 1){
39413                 return;
39414             }
39415             
39416             if(!match){
39417                 filterPattern(box, length - 1);
39418                 return;
39419             }
39420                 
39421             queue.push(pattern);
39422
39423             box = box.slice(length, box.length);
39424
39425             filterPattern(box, 4);
39426
39427             return;
39428             
39429         }
39430         
39431         Roo.each(boxes, function(box, k){
39432             
39433             if(!box.length){
39434                 return;
39435             }
39436             
39437             if(box.length == 1){
39438                 queue.push(box);
39439                 return;
39440             }
39441             
39442             filterPattern(box, 4);
39443             
39444         }, this);
39445         
39446         this._processVerticalLayoutQueue( queue, isInstant );
39447         
39448     },
39449     
39450 //    _verticalAlternativeLayoutItems : function( items , isInstant )
39451 //    {
39452 //        if ( !items || !items.length ) {
39453 //            return;
39454 //        }
39455 //
39456 //        this._processVerticalAlternativeLayoutQueue( items, isInstant );
39457 //        
39458 //    },
39459     
39460     _horizontalLayoutItems : function ( items , isInstant)
39461     {
39462         if ( !items || !items.length || items.length < 3) {
39463             return;
39464         }
39465         
39466         items.reverse();
39467         
39468         var eItems = items.slice(0, 3);
39469         
39470         items = items.slice(3, items.length);
39471         
39472         var standard = [
39473             ['xs', 'xs', 'xs', 'wide'],
39474             ['xs', 'xs', 'wide'],
39475             ['xs', 'xs', 'sm'],
39476             ['xs', 'xs', 'xs'],
39477             ['xs', 'wide'],
39478             ['xs', 'sm'],
39479             ['xs', 'xs'],
39480             ['xs'],
39481             
39482             ['sm', 'xs', 'xs'],
39483             ['sm', 'xs'],
39484             ['sm'],
39485             
39486             ['wide', 'xs', 'xs', 'xs'],
39487             ['wide', 'xs', 'xs'],
39488             ['wide', 'xs'],
39489             ['wide'],
39490             
39491             ['wide-thin']
39492         ];
39493         
39494         var queue = [];
39495         
39496         var boxes = [];
39497         
39498         var box = [];
39499         
39500         Roo.each(items, function(item, k){
39501             
39502             switch (item.size) {
39503                 case 'md' :
39504                 case 'md-left' :
39505                 case 'md-right' :
39506                 case 'tall' :
39507                     
39508                     if(box.length){
39509                         boxes.push(box);
39510                         box = [];
39511                     }
39512                     
39513                     boxes.push([item]);
39514                     
39515                     break;
39516                     
39517                 case 'xs' :
39518                 case 'sm' :
39519                 case 'wide' :
39520                 case 'wide-thin' :
39521                     
39522                     box.push(item);
39523                     
39524                     break;
39525                 default :
39526                     break;
39527                     
39528             }
39529             
39530         }, this);
39531         
39532         if(box.length){
39533             boxes.push(box);
39534             box = [];
39535         }
39536         
39537         var filterPattern = function(box, length)
39538         {
39539             if(!box.length){
39540                 return;
39541             }
39542             
39543             var match = false;
39544             
39545             var pattern = box.slice(0, length);
39546             
39547             var format = [];
39548             
39549             Roo.each(pattern, function(i){
39550                 format.push(i.size);
39551             }, this);
39552             
39553             Roo.each(standard, function(s){
39554                 
39555                 if(String(s) != String(format)){
39556                     return;
39557                 }
39558                 
39559                 match = true;
39560                 return false;
39561                 
39562             }, this);
39563             
39564             if(!match && length == 1){
39565                 return;
39566             }
39567             
39568             if(!match){
39569                 filterPattern(box, length - 1);
39570                 return;
39571             }
39572                 
39573             queue.push(pattern);
39574
39575             box = box.slice(length, box.length);
39576
39577             filterPattern(box, 4);
39578
39579             return;
39580             
39581         }
39582         
39583         Roo.each(boxes, function(box, k){
39584             
39585             if(!box.length){
39586                 return;
39587             }
39588             
39589             if(box.length == 1){
39590                 queue.push(box);
39591                 return;
39592             }
39593             
39594             filterPattern(box, 4);
39595             
39596         }, this);
39597         
39598         
39599         var prune = [];
39600         
39601         var pos = this.el.getBox(true);
39602         
39603         var minX = pos.x;
39604         
39605         var maxX = pos.right - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
39606         
39607         var hit_end = false;
39608         
39609         Roo.each(queue, function(box){
39610             
39611             if(hit_end){
39612                 
39613                 Roo.each(box, function(b){
39614                 
39615                     b.el.setVisibilityMode(Roo.Element.DISPLAY);
39616                     b.el.hide();
39617
39618                 }, this);
39619
39620                 return;
39621             }
39622             
39623             var mx = 0;
39624             
39625             Roo.each(box, function(b){
39626                 
39627                 b.el.setVisibilityMode(Roo.Element.DISPLAY);
39628                 b.el.show();
39629
39630                 mx = Math.max(mx, b.x);
39631                 
39632             }, this);
39633             
39634             maxX = maxX - this.unitWidth * mx - this.gutter * (mx - 1) - this.padWidth;
39635             
39636             if(maxX < minX){
39637                 
39638                 Roo.each(box, function(b){
39639                 
39640                     b.el.setVisibilityMode(Roo.Element.DISPLAY);
39641                     b.el.hide();
39642                     
39643                 }, this);
39644                 
39645                 hit_end = true;
39646                 
39647                 return;
39648             }
39649             
39650             prune.push(box);
39651             
39652         }, this);
39653         
39654         this._processHorizontalLayoutQueue( prune, eItems, isInstant );
39655     },
39656     
39657     /** Sets position of item in DOM
39658     * @param {Element} item
39659     * @param {Number} x - horizontal position
39660     * @param {Number} y - vertical position
39661     * @param {Boolean} isInstant - disables transitions
39662     */
39663     _processVerticalLayoutQueue : function( queue, isInstant )
39664     {
39665         var pos = this.el.getBox(true);
39666         var x = pos.x;
39667         var y = pos.y;
39668         var maxY = [];
39669         
39670         for (var i = 0; i < this.cols; i++){
39671             maxY[i] = pos.y;
39672         }
39673         
39674         Roo.each(queue, function(box, k){
39675             
39676             var col = k % this.cols;
39677             
39678             Roo.each(box, function(b,kk){
39679                 
39680                 b.el.position('absolute');
39681                 
39682                 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
39683                 var height = Math.floor(this.unitHeight * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
39684                 
39685                 if(b.size == 'md-left' || b.size == 'md-right'){
39686                     width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
39687                     height = Math.floor(this.unitHeight * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
39688                 }
39689                 
39690                 b.el.setWidth(width);
39691                 b.el.setHeight(height);
39692                 // iframe?
39693                 b.el.select('iframe',true).setSize(width,height);
39694                 
39695             }, this);
39696             
39697             for (var i = 0; i < this.cols; i++){
39698                 
39699                 if(maxY[i] < maxY[col]){
39700                     col = i;
39701                     continue;
39702                 }
39703                 
39704                 col = Math.min(col, i);
39705                 
39706             }
39707             
39708             x = pos.x + col * (this.colWidth + this.padWidth);
39709             
39710             y = maxY[col];
39711             
39712             var positions = [];
39713             
39714             switch (box.length){
39715                 case 1 :
39716                     positions = this.getVerticalOneBoxColPositions(x, y, box);
39717                     break;
39718                 case 2 :
39719                     positions = this.getVerticalTwoBoxColPositions(x, y, box);
39720                     break;
39721                 case 3 :
39722                     positions = this.getVerticalThreeBoxColPositions(x, y, box);
39723                     break;
39724                 case 4 :
39725                     positions = this.getVerticalFourBoxColPositions(x, y, box);
39726                     break;
39727                 default :
39728                     break;
39729             }
39730             
39731             Roo.each(box, function(b,kk){
39732                 
39733                 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
39734                 
39735                 var sz = b.el.getSize();
39736                 
39737                 maxY[col] = Math.max(maxY[col], positions[kk].y + sz.height + this.padWidth);
39738                 
39739             }, this);
39740             
39741         }, this);
39742         
39743         var mY = 0;
39744         
39745         for (var i = 0; i < this.cols; i++){
39746             mY = Math.max(mY, maxY[i]);
39747         }
39748         
39749         this.el.setHeight(mY - pos.y);
39750         
39751     },
39752     
39753 //    _processVerticalAlternativeLayoutQueue : function( items, isInstant )
39754 //    {
39755 //        var pos = this.el.getBox(true);
39756 //        var x = pos.x;
39757 //        var y = pos.y;
39758 //        var maxX = pos.right;
39759 //        
39760 //        var maxHeight = 0;
39761 //        
39762 //        Roo.each(items, function(item, k){
39763 //            
39764 //            var c = k % 2;
39765 //            
39766 //            item.el.position('absolute');
39767 //                
39768 //            var width = Math.floor(this.colWidth + item.el.getPadding('lr'));
39769 //
39770 //            item.el.setWidth(width);
39771 //
39772 //            var height = Math.floor(this.colWidth * item.y / item.x + item.el.getPadding('tb'));
39773 //
39774 //            item.el.setHeight(height);
39775 //            
39776 //            if(c == 0){
39777 //                item.el.setXY([x, y], isInstant ? false : true);
39778 //            } else {
39779 //                item.el.setXY([maxX - width, y], isInstant ? false : true);
39780 //            }
39781 //            
39782 //            y = y + height + this.alternativePadWidth;
39783 //            
39784 //            maxHeight = maxHeight + height + this.alternativePadWidth;
39785 //            
39786 //        }, this);
39787 //        
39788 //        this.el.setHeight(maxHeight);
39789 //        
39790 //    },
39791     
39792     _processHorizontalLayoutQueue : function( queue, eItems, isInstant )
39793     {
39794         var pos = this.el.getBox(true);
39795         
39796         var minX = pos.x;
39797         var minY = pos.y;
39798         
39799         var maxX = pos.right;
39800         
39801         this._processHorizontalEndItem(eItems, maxX, minX, minY, isInstant);
39802         
39803         var maxX = maxX - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
39804         
39805         Roo.each(queue, function(box, k){
39806             
39807             Roo.each(box, function(b, kk){
39808                 
39809                 b.el.position('absolute');
39810                 
39811                 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
39812                 var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
39813                 
39814                 if(b.size == 'md-left' || b.size == 'md-right'){
39815                     width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
39816                     height = Math.floor(this.unitWidth * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
39817                 }
39818                 
39819                 b.el.setWidth(width);
39820                 b.el.setHeight(height);
39821                 
39822             }, this);
39823             
39824             if(!box.length){
39825                 return;
39826             }
39827             
39828             var positions = [];
39829             
39830             switch (box.length){
39831                 case 1 :
39832                     positions = this.getHorizontalOneBoxColPositions(maxX, minY, box);
39833                     break;
39834                 case 2 :
39835                     positions = this.getHorizontalTwoBoxColPositions(maxX, minY, box);
39836                     break;
39837                 case 3 :
39838                     positions = this.getHorizontalThreeBoxColPositions(maxX, minY, box);
39839                     break;
39840                 case 4 :
39841                     positions = this.getHorizontalFourBoxColPositions(maxX, minY, box);
39842                     break;
39843                 default :
39844                     break;
39845             }
39846             
39847             Roo.each(box, function(b,kk){
39848                 
39849                 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
39850                 
39851                 maxX = Math.min(maxX, positions[kk].x - this.padWidth);
39852                 
39853             }, this);
39854             
39855         }, this);
39856         
39857     },
39858     
39859     _processHorizontalEndItem : function(eItems, maxX, minX, minY, isInstant)
39860     {
39861         Roo.each(eItems, function(b,k){
39862             
39863             b.size = (k == 0) ? 'sm' : 'xs';
39864             b.x = (k == 0) ? 2 : 1;
39865             b.y = (k == 0) ? 2 : 1;
39866             
39867             b.el.position('absolute');
39868             
39869             var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
39870                 
39871             b.el.setWidth(width);
39872             
39873             var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
39874             
39875             b.el.setHeight(height);
39876             
39877         }, this);
39878
39879         var positions = [];
39880         
39881         positions.push({
39882             x : maxX - this.unitWidth * 2 - this.gutter,
39883             y : minY
39884         });
39885         
39886         positions.push({
39887             x : maxX - this.unitWidth,
39888             y : minY + (this.unitWidth + this.gutter) * 2
39889         });
39890         
39891         positions.push({
39892             x : maxX - this.unitWidth * 3 - this.gutter * 2,
39893             y : minY
39894         });
39895         
39896         Roo.each(eItems, function(b,k){
39897             
39898             b.el.setXY([positions[k].x, positions[k].y], isInstant ? false : true);
39899
39900         }, this);
39901         
39902     },
39903     
39904     getVerticalOneBoxColPositions : function(x, y, box)
39905     {
39906         var pos = [];
39907         
39908         var rand = Math.floor(Math.random() * ((4 - box[0].x)));
39909         
39910         if(box[0].size == 'md-left'){
39911             rand = 0;
39912         }
39913         
39914         if(box[0].size == 'md-right'){
39915             rand = 1;
39916         }
39917         
39918         pos.push({
39919             x : x + (this.unitWidth + this.gutter) * rand,
39920             y : y
39921         });
39922         
39923         return pos;
39924     },
39925     
39926     getVerticalTwoBoxColPositions : function(x, y, box)
39927     {
39928         var pos = [];
39929         
39930         if(box[0].size == 'xs'){
39931             
39932             pos.push({
39933                 x : x,
39934                 y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[1].y))
39935             });
39936
39937             pos.push({
39938                 x : x + (this.unitWidth + this.gutter) * (3 - box[1].x),
39939                 y : y
39940             });
39941             
39942             return pos;
39943             
39944         }
39945         
39946         pos.push({
39947             x : x,
39948             y : y
39949         });
39950
39951         pos.push({
39952             x : x + (this.unitWidth + this.gutter) * 2,
39953             y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[0].y))
39954         });
39955         
39956         return pos;
39957         
39958     },
39959     
39960     getVerticalThreeBoxColPositions : function(x, y, box)
39961     {
39962         var pos = [];
39963         
39964         if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
39965             
39966             pos.push({
39967                 x : x,
39968                 y : y
39969             });
39970
39971             pos.push({
39972                 x : x + (this.unitWidth + this.gutter) * 1,
39973                 y : y
39974             });
39975             
39976             pos.push({
39977                 x : x + (this.unitWidth + this.gutter) * 2,
39978                 y : y
39979             });
39980             
39981             return pos;
39982             
39983         }
39984         
39985         if(box[0].size == 'xs' && box[1].size == 'xs'){
39986             
39987             pos.push({
39988                 x : x,
39989                 y : y
39990             });
39991
39992             pos.push({
39993                 x : x,
39994                 y : y + ((this.unitHeight + this.gutter) * (box[2].y - 1))
39995             });
39996             
39997             pos.push({
39998                 x : x + (this.unitWidth + this.gutter) * 1,
39999                 y : y
40000             });
40001             
40002             return pos;
40003             
40004         }
40005         
40006         pos.push({
40007             x : x,
40008             y : y
40009         });
40010
40011         pos.push({
40012             x : x + (this.unitWidth + this.gutter) * 2,
40013             y : y
40014         });
40015
40016         pos.push({
40017             x : x + (this.unitWidth + this.gutter) * 2,
40018             y : y + (this.unitHeight + this.gutter) * (box[0].y - 1)
40019         });
40020             
40021         return pos;
40022         
40023     },
40024     
40025     getVerticalFourBoxColPositions : function(x, y, box)
40026     {
40027         var pos = [];
40028         
40029         if(box[0].size == 'xs'){
40030             
40031             pos.push({
40032                 x : x,
40033                 y : y
40034             });
40035
40036             pos.push({
40037                 x : x,
40038                 y : y + (this.unitHeight + this.gutter) * 1
40039             });
40040             
40041             pos.push({
40042                 x : x,
40043                 y : y + (this.unitHeight + this.gutter) * 2
40044             });
40045             
40046             pos.push({
40047                 x : x + (this.unitWidth + this.gutter) * 1,
40048                 y : y
40049             });
40050             
40051             return pos;
40052             
40053         }
40054         
40055         pos.push({
40056             x : x,
40057             y : y
40058         });
40059
40060         pos.push({
40061             x : x + (this.unitWidth + this.gutter) * 2,
40062             y : y
40063         });
40064
40065         pos.push({
40066             x : x + (this.unitHeightunitWidth + this.gutter) * 2,
40067             y : y + (this.unitHeight + this.gutter) * 1
40068         });
40069
40070         pos.push({
40071             x : x + (this.unitWidth + this.gutter) * 2,
40072             y : y + (this.unitWidth + this.gutter) * 2
40073         });
40074
40075         return pos;
40076         
40077     },
40078     
40079     getHorizontalOneBoxColPositions : function(maxX, minY, box)
40080     {
40081         var pos = [];
40082         
40083         if(box[0].size == 'md-left'){
40084             pos.push({
40085                 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
40086                 y : minY
40087             });
40088             
40089             return pos;
40090         }
40091         
40092         if(box[0].size == 'md-right'){
40093             pos.push({
40094                 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
40095                 y : minY + (this.unitWidth + this.gutter) * 1
40096             });
40097             
40098             return pos;
40099         }
40100         
40101         var rand = Math.floor(Math.random() * (4 - box[0].y));
40102         
40103         pos.push({
40104             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
40105             y : minY + (this.unitWidth + this.gutter) * rand
40106         });
40107         
40108         return pos;
40109         
40110     },
40111     
40112     getHorizontalTwoBoxColPositions : function(maxX, minY, box)
40113     {
40114         var pos = [];
40115         
40116         if(box[0].size == 'xs'){
40117             
40118             pos.push({
40119                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
40120                 y : minY
40121             });
40122
40123             pos.push({
40124                 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
40125                 y : minY + (this.unitWidth + this.gutter) * (3 - box[1].y)
40126             });
40127             
40128             return pos;
40129             
40130         }
40131         
40132         pos.push({
40133             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
40134             y : minY
40135         });
40136
40137         pos.push({
40138             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
40139             y : minY + (this.unitWidth + this.gutter) * 2
40140         });
40141         
40142         return pos;
40143         
40144     },
40145     
40146     getHorizontalThreeBoxColPositions : function(maxX, minY, box)
40147     {
40148         var pos = [];
40149         
40150         if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
40151             
40152             pos.push({
40153                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
40154                 y : minY
40155             });
40156
40157             pos.push({
40158                 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
40159                 y : minY + (this.unitWidth + this.gutter) * 1
40160             });
40161             
40162             pos.push({
40163                 x : maxX - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
40164                 y : minY + (this.unitWidth + this.gutter) * 2
40165             });
40166             
40167             return pos;
40168             
40169         }
40170         
40171         if(box[0].size == 'xs' && box[1].size == 'xs'){
40172             
40173             pos.push({
40174                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
40175                 y : minY
40176             });
40177
40178             pos.push({
40179                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
40180                 y : minY
40181             });
40182             
40183             pos.push({
40184                 x : maxX - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
40185                 y : minY + (this.unitWidth + this.gutter) * 1
40186             });
40187             
40188             return pos;
40189             
40190         }
40191         
40192         pos.push({
40193             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
40194             y : minY
40195         });
40196
40197         pos.push({
40198             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
40199             y : minY + (this.unitWidth + this.gutter) * 2
40200         });
40201
40202         pos.push({
40203             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
40204             y : minY + (this.unitWidth + this.gutter) * 2
40205         });
40206             
40207         return pos;
40208         
40209     },
40210     
40211     getHorizontalFourBoxColPositions : function(maxX, minY, box)
40212     {
40213         var pos = [];
40214         
40215         if(box[0].size == 'xs'){
40216             
40217             pos.push({
40218                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
40219                 y : minY
40220             });
40221
40222             pos.push({
40223                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
40224                 y : minY
40225             });
40226             
40227             pos.push({
40228                 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),
40229                 y : minY
40230             });
40231             
40232             pos.push({
40233                 x : maxX - this.unitWidth * box[3].x - this.gutter * (box[3].x - 1),
40234                 y : minY + (this.unitWidth + this.gutter) * 1
40235             });
40236             
40237             return pos;
40238             
40239         }
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[1].x - this.gutter * (box[1].x - 1),
40248             y : minY + (this.unitWidth + this.gutter) * 2
40249         });
40250         
40251         pos.push({
40252             x : maxX - 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 + (this.unitWidth + this.gutter) * 2
40254         });
40255         
40256         pos.push({
40257             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),
40258             y : minY + (this.unitWidth + this.gutter) * 2
40259         });
40260
40261         return pos;
40262         
40263     },
40264     
40265     /**
40266     * remove a Masonry Brick
40267     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to remove
40268     */
40269     removeBrick : function(brick_id)
40270     {
40271         if (!brick_id) {
40272             return;
40273         }
40274         
40275         for (var i = 0; i<this.bricks.length; i++) {
40276             if (this.bricks[i].id == brick_id) {
40277                 this.bricks.splice(i,1);
40278                 this.el.dom.removeChild(Roo.get(brick_id).dom);
40279                 this.initial();
40280             }
40281         }
40282     },
40283     
40284     /**
40285     * adds a Masonry Brick
40286     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
40287     */
40288     addBrick : function(cfg)
40289     {
40290         var cn = new Roo.bootstrap.MasonryBrick(cfg);
40291         //this.register(cn);
40292         cn.parentId = this.id;
40293         cn.render(this.el);
40294         return cn;
40295     },
40296     
40297     /**
40298     * register a Masonry Brick
40299     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
40300     */
40301     
40302     register : function(brick)
40303     {
40304         this.bricks.push(brick);
40305         brick.masonryId = this.id;
40306     },
40307     
40308     /**
40309     * clear all the Masonry Brick
40310     */
40311     clearAll : function()
40312     {
40313         this.bricks = [];
40314         //this.getChildContainer().dom.innerHTML = "";
40315         this.el.dom.innerHTML = '';
40316     },
40317     
40318     getSelected : function()
40319     {
40320         if (!this.selectedBrick) {
40321             return false;
40322         }
40323         
40324         return this.selectedBrick;
40325     }
40326 });
40327
40328 Roo.apply(Roo.bootstrap.LayoutMasonry, {
40329     
40330     groups: {},
40331      /**
40332     * register a Masonry Layout
40333     * @param {Roo.bootstrap.LayoutMasonry} the masonry layout to add
40334     */
40335     
40336     register : function(layout)
40337     {
40338         this.groups[layout.id] = layout;
40339     },
40340     /**
40341     * fetch a  Masonry Layout based on the masonry layout ID
40342     * @param {string} the masonry layout to add
40343     * @returns {Roo.bootstrap.LayoutMasonry} the masonry layout
40344     */
40345     
40346     get: function(layout_id) {
40347         if (typeof(this.groups[layout_id]) == 'undefined') {
40348             return false;
40349         }
40350         return this.groups[layout_id] ;
40351     }
40352     
40353     
40354     
40355 });
40356
40357  
40358
40359  /**
40360  *
40361  * This is based on 
40362  * http://masonry.desandro.com
40363  *
40364  * The idea is to render all the bricks based on vertical width...
40365  *
40366  * The original code extends 'outlayer' - we might need to use that....
40367  * 
40368  */
40369
40370
40371 /**
40372  * @class Roo.bootstrap.LayoutMasonryAuto
40373  * @extends Roo.bootstrap.Component
40374  * Bootstrap Layout Masonry class
40375  * 
40376  * @constructor
40377  * Create a new Element
40378  * @param {Object} config The config object
40379  */
40380
40381 Roo.bootstrap.LayoutMasonryAuto = function(config){
40382     Roo.bootstrap.LayoutMasonryAuto.superclass.constructor.call(this, config);
40383 };
40384
40385 Roo.extend(Roo.bootstrap.LayoutMasonryAuto, Roo.bootstrap.Component,  {
40386     
40387       /**
40388      * @cfg {Boolean} isFitWidth  - resize the width..
40389      */   
40390     isFitWidth : false,  // options..
40391     /**
40392      * @cfg {Boolean} isOriginLeft = left align?
40393      */   
40394     isOriginLeft : true,
40395     /**
40396      * @cfg {Boolean} isOriginTop = top align?
40397      */   
40398     isOriginTop : false,
40399     /**
40400      * @cfg {Boolean} isLayoutInstant = no animation?
40401      */   
40402     isLayoutInstant : false, // needed?
40403     /**
40404      * @cfg {Boolean} isResizingContainer = not sure if this is used..
40405      */   
40406     isResizingContainer : true,
40407     /**
40408      * @cfg {Number} columnWidth  width of the columns 
40409      */   
40410     
40411     columnWidth : 0,
40412     
40413     /**
40414      * @cfg {Number} maxCols maximum number of columns
40415      */   
40416     
40417     maxCols: 0,
40418     /**
40419      * @cfg {Number} padHeight padding below box..
40420      */   
40421     
40422     padHeight : 10, 
40423     
40424     /**
40425      * @cfg {Boolean} isAutoInitial defalut true
40426      */   
40427     
40428     isAutoInitial : true, 
40429     
40430     // private?
40431     gutter : 0,
40432     
40433     containerWidth: 0,
40434     initialColumnWidth : 0,
40435     currentSize : null,
40436     
40437     colYs : null, // array.
40438     maxY : 0,
40439     padWidth: 10,
40440     
40441     
40442     tag: 'div',
40443     cls: '',
40444     bricks: null, //CompositeElement
40445     cols : 0, // array?
40446     // element : null, // wrapped now this.el
40447     _isLayoutInited : null, 
40448     
40449     
40450     getAutoCreate : function(){
40451         
40452         var cfg = {
40453             tag: this.tag,
40454             cls: 'blog-masonary-wrapper ' + this.cls,
40455             cn : {
40456                 cls : 'mas-boxes masonary'
40457             }
40458         };
40459         
40460         return cfg;
40461     },
40462     
40463     getChildContainer: function( )
40464     {
40465         if (this.boxesEl) {
40466             return this.boxesEl;
40467         }
40468         
40469         this.boxesEl = this.el.select('.mas-boxes').first();
40470         
40471         return this.boxesEl;
40472     },
40473     
40474     
40475     initEvents : function()
40476     {
40477         var _this = this;
40478         
40479         if(this.isAutoInitial){
40480             Roo.log('hook children rendered');
40481             this.on('childrenrendered', function() {
40482                 Roo.log('children rendered');
40483                 _this.initial();
40484             } ,this);
40485         }
40486         
40487     },
40488     
40489     initial : function()
40490     {
40491         this.reloadItems();
40492
40493         this.currentSize = this.el.getBox(true);
40494
40495         /// was window resize... - let's see if this works..
40496         Roo.EventManager.onWindowResize(this.resize, this); 
40497
40498         if(!this.isAutoInitial){
40499             this.layout();
40500             return;
40501         }
40502         
40503         this.layout.defer(500,this);
40504     },
40505     
40506     reloadItems: function()
40507     {
40508         this.bricks = this.el.select('.masonry-brick', true);
40509         
40510         this.bricks.each(function(b) {
40511             //Roo.log(b.getSize());
40512             if (!b.attr('originalwidth')) {
40513                 b.attr('originalwidth',  b.getSize().width);
40514             }
40515             
40516         });
40517         
40518         Roo.log(this.bricks.elements.length);
40519     },
40520     
40521     resize : function()
40522     {
40523         Roo.log('resize');
40524         var cs = this.el.getBox(true);
40525         
40526         if (this.currentSize.width == cs.width && this.currentSize.x == cs.x ) {
40527             Roo.log("no change in with or X");
40528             return;
40529         }
40530         this.currentSize = cs;
40531         this.layout();
40532     },
40533     
40534     layout : function()
40535     {
40536          Roo.log('layout');
40537         this._resetLayout();
40538         //this._manageStamps();
40539       
40540         // don't animate first layout
40541         var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
40542         this.layoutItems( isInstant );
40543       
40544         // flag for initalized
40545         this._isLayoutInited = true;
40546     },
40547     
40548     layoutItems : function( isInstant )
40549     {
40550         //var items = this._getItemsForLayout( this.items );
40551         // original code supports filtering layout items.. we just ignore it..
40552         
40553         this._layoutItems( this.bricks , isInstant );
40554       
40555         this._postLayout();
40556     },
40557     _layoutItems : function ( items , isInstant)
40558     {
40559        //this.fireEvent( 'layout', this, items );
40560     
40561
40562         if ( !items || !items.elements.length ) {
40563           // no items, emit event with empty array
40564             return;
40565         }
40566
40567         var queue = [];
40568         items.each(function(item) {
40569             Roo.log("layout item");
40570             Roo.log(item);
40571             // get x/y object from method
40572             var position = this._getItemLayoutPosition( item );
40573             // enqueue
40574             position.item = item;
40575             position.isInstant = isInstant; // || item.isLayoutInstant; << not set yet...
40576             queue.push( position );
40577         }, this);
40578       
40579         this._processLayoutQueue( queue );
40580     },
40581     /** Sets position of item in DOM
40582     * @param {Element} item
40583     * @param {Number} x - horizontal position
40584     * @param {Number} y - vertical position
40585     * @param {Boolean} isInstant - disables transitions
40586     */
40587     _processLayoutQueue : function( queue )
40588     {
40589         for ( var i=0, len = queue.length; i < len; i++ ) {
40590             var obj = queue[i];
40591             obj.item.position('absolute');
40592             obj.item.setXY([obj.x,obj.y], obj.isInstant ? false : true);
40593         }
40594     },
40595       
40596     
40597     /**
40598     * Any logic you want to do after each layout,
40599     * i.e. size the container
40600     */
40601     _postLayout : function()
40602     {
40603         this.resizeContainer();
40604     },
40605     
40606     resizeContainer : function()
40607     {
40608         if ( !this.isResizingContainer ) {
40609             return;
40610         }
40611         var size = this._getContainerSize();
40612         if ( size ) {
40613             this.el.setSize(size.width,size.height);
40614             this.boxesEl.setSize(size.width,size.height);
40615         }
40616     },
40617     
40618     
40619     
40620     _resetLayout : function()
40621     {
40622         //this.getSize();  // -- does not really do anything.. it probably applies left/right etc. to obuject but not used
40623         this.colWidth = this.el.getWidth();
40624         //this.gutter = this.el.getWidth(); 
40625         
40626         this.measureColumns();
40627
40628         // reset column Y
40629         var i = this.cols;
40630         this.colYs = [];
40631         while (i--) {
40632             this.colYs.push( 0 );
40633         }
40634     
40635         this.maxY = 0;
40636     },
40637
40638     measureColumns : function()
40639     {
40640         this.getContainerWidth();
40641       // if columnWidth is 0, default to outerWidth of first item
40642         if ( !this.columnWidth ) {
40643             var firstItem = this.bricks.first();
40644             Roo.log(firstItem);
40645             this.columnWidth  = this.containerWidth;
40646             if (firstItem && firstItem.attr('originalwidth') ) {
40647                 this.columnWidth = 1* (firstItem.attr('originalwidth') || firstItem.getWidth());
40648             }
40649             // columnWidth fall back to item of first element
40650             Roo.log("set column width?");
40651                         this.initialColumnWidth = this.columnWidth  ;
40652
40653             // if first elem has no width, default to size of container
40654             
40655         }
40656         
40657         
40658         if (this.initialColumnWidth) {
40659             this.columnWidth = this.initialColumnWidth;
40660         }
40661         
40662         
40663             
40664         // column width is fixed at the top - however if container width get's smaller we should
40665         // reduce it...
40666         
40667         // this bit calcs how man columns..
40668             
40669         var columnWidth = this.columnWidth += this.gutter;
40670       
40671         // calculate columns
40672         var containerWidth = this.containerWidth + this.gutter;
40673         
40674         var cols = (containerWidth - this.padWidth) / (columnWidth - this.padWidth);
40675         // fix rounding errors, typically with gutters
40676         var excess = columnWidth - containerWidth % columnWidth;
40677         
40678         
40679         // if overshoot is less than a pixel, round up, otherwise floor it
40680         var mathMethod = excess && excess < 1 ? 'round' : 'floor';
40681         cols = Math[ mathMethod ]( cols );
40682         this.cols = Math.max( cols, 1 );
40683         this.cols = this.maxCols > 0 ? Math.min( this.cols, this.maxCols ) : this.cols;
40684         
40685          // padding positioning..
40686         var totalColWidth = this.cols * this.columnWidth;
40687         var padavail = this.containerWidth - totalColWidth;
40688         // so for 2 columns - we need 3 'pads'
40689         
40690         var padNeeded = (1+this.cols) * this.padWidth;
40691         
40692         var padExtra = Math.floor((padavail - padNeeded) / this.cols);
40693         
40694         this.columnWidth += padExtra
40695         //this.padWidth = Math.floor(padavail /  ( this.cols));
40696         
40697         // adjust colum width so that padding is fixed??
40698         
40699         // we have 3 columns ... total = width * 3
40700         // we have X left over... that should be used by 
40701         
40702         //if (this.expandC) {
40703             
40704         //}
40705         
40706         
40707         
40708     },
40709     
40710     getContainerWidth : function()
40711     {
40712        /* // container is parent if fit width
40713         var container = this.isFitWidth ? this.element.parentNode : this.element;
40714         // check that this.size and size are there
40715         // IE8 triggers resize on body size change, so they might not be
40716         
40717         var size = getSize( container );  //FIXME
40718         this.containerWidth = size && size.innerWidth; //FIXME
40719         */
40720          
40721         this.containerWidth = this.el.getBox(true).width;  //maybe use getComputedWidth
40722         
40723     },
40724     
40725     _getItemLayoutPosition : function( item )  // what is item?
40726     {
40727         // we resize the item to our columnWidth..
40728       
40729         item.setWidth(this.columnWidth);
40730         item.autoBoxAdjust  = false;
40731         
40732         var sz = item.getSize();
40733  
40734         // how many columns does this brick span
40735         var remainder = this.containerWidth % this.columnWidth;
40736         
40737         var mathMethod = remainder && remainder < 1 ? 'round' : 'ceil';
40738         // round if off by 1 pixel, otherwise use ceil
40739         var colSpan = Math[ mathMethod ]( sz.width  / this.columnWidth );
40740         colSpan = Math.min( colSpan, this.cols );
40741         
40742         // normally this should be '1' as we dont' currently allow multi width columns..
40743         
40744         var colGroup = this._getColGroup( colSpan );
40745         // get the minimum Y value from the columns
40746         var minimumY = Math.min.apply( Math, colGroup );
40747         Roo.log([ 'setHeight',  minimumY, sz.height, setHeight ]);
40748         
40749         var shortColIndex = colGroup.indexOf(  minimumY ); // broken on ie8..?? probably...
40750          
40751         // position the brick
40752         var position = {
40753             x: this.currentSize.x + (this.padWidth /2) + ((this.columnWidth + this.padWidth )* shortColIndex),
40754             y: this.currentSize.y + minimumY + this.padHeight
40755         };
40756         
40757         Roo.log(position);
40758         // apply setHeight to necessary columns
40759         var setHeight = minimumY + sz.height + this.padHeight;
40760         //Roo.log([ 'setHeight',  minimumY, sz.height, setHeight ]);
40761         
40762         var setSpan = this.cols + 1 - colGroup.length;
40763         for ( var i = 0; i < setSpan; i++ ) {
40764           this.colYs[ shortColIndex + i ] = setHeight ;
40765         }
40766       
40767         return position;
40768     },
40769     
40770     /**
40771      * @param {Number} colSpan - number of columns the element spans
40772      * @returns {Array} colGroup
40773      */
40774     _getColGroup : function( colSpan )
40775     {
40776         if ( colSpan < 2 ) {
40777           // if brick spans only one column, use all the column Ys
40778           return this.colYs;
40779         }
40780       
40781         var colGroup = [];
40782         // how many different places could this brick fit horizontally
40783         var groupCount = this.cols + 1 - colSpan;
40784         // for each group potential horizontal position
40785         for ( var i = 0; i < groupCount; i++ ) {
40786           // make an array of colY values for that one group
40787           var groupColYs = this.colYs.slice( i, i + colSpan );
40788           // and get the max value of the array
40789           colGroup[i] = Math.max.apply( Math, groupColYs );
40790         }
40791         return colGroup;
40792     },
40793     /*
40794     _manageStamp : function( stamp )
40795     {
40796         var stampSize =  stamp.getSize();
40797         var offset = stamp.getBox();
40798         // get the columns that this stamp affects
40799         var firstX = this.isOriginLeft ? offset.x : offset.right;
40800         var lastX = firstX + stampSize.width;
40801         var firstCol = Math.floor( firstX / this.columnWidth );
40802         firstCol = Math.max( 0, firstCol );
40803         
40804         var lastCol = Math.floor( lastX / this.columnWidth );
40805         // lastCol should not go over if multiple of columnWidth #425
40806         lastCol -= lastX % this.columnWidth ? 0 : 1;
40807         lastCol = Math.min( this.cols - 1, lastCol );
40808         
40809         // set colYs to bottom of the stamp
40810         var stampMaxY = ( this.isOriginTop ? offset.y : offset.bottom ) +
40811             stampSize.height;
40812             
40813         for ( var i = firstCol; i <= lastCol; i++ ) {
40814           this.colYs[i] = Math.max( stampMaxY, this.colYs[i] );
40815         }
40816     },
40817     */
40818     
40819     _getContainerSize : function()
40820     {
40821         this.maxY = Math.max.apply( Math, this.colYs );
40822         var size = {
40823             height: this.maxY
40824         };
40825       
40826         if ( this.isFitWidth ) {
40827             size.width = this._getContainerFitWidth();
40828         }
40829       
40830         return size;
40831     },
40832     
40833     _getContainerFitWidth : function()
40834     {
40835         var unusedCols = 0;
40836         // count unused columns
40837         var i = this.cols;
40838         while ( --i ) {
40839           if ( this.colYs[i] !== 0 ) {
40840             break;
40841           }
40842           unusedCols++;
40843         }
40844         // fit container to columns that have been used
40845         return ( this.cols - unusedCols ) * this.columnWidth - this.gutter;
40846     },
40847     
40848     needsResizeLayout : function()
40849     {
40850         var previousWidth = this.containerWidth;
40851         this.getContainerWidth();
40852         return previousWidth !== this.containerWidth;
40853     }
40854  
40855 });
40856
40857  
40858
40859  /*
40860  * - LGPL
40861  *
40862  * element
40863  * 
40864  */
40865
40866 /**
40867  * @class Roo.bootstrap.MasonryBrick
40868  * @extends Roo.bootstrap.Component
40869  * Bootstrap MasonryBrick class
40870  * 
40871  * @constructor
40872  * Create a new MasonryBrick
40873  * @param {Object} config The config object
40874  */
40875
40876 Roo.bootstrap.MasonryBrick = function(config){
40877     
40878     Roo.bootstrap.MasonryBrick.superclass.constructor.call(this, config);
40879     
40880     Roo.bootstrap.MasonryBrick.register(this);
40881     
40882     this.addEvents({
40883         // raw events
40884         /**
40885          * @event click
40886          * When a MasonryBrick is clcik
40887          * @param {Roo.bootstrap.MasonryBrick} this
40888          * @param {Roo.EventObject} e
40889          */
40890         "click" : true
40891     });
40892 };
40893
40894 Roo.extend(Roo.bootstrap.MasonryBrick, Roo.bootstrap.Component,  {
40895     
40896     /**
40897      * @cfg {String} title
40898      */   
40899     title : '',
40900     /**
40901      * @cfg {String} html
40902      */   
40903     html : '',
40904     /**
40905      * @cfg {String} bgimage
40906      */   
40907     bgimage : '',
40908     /**
40909      * @cfg {String} videourl
40910      */   
40911     videourl : '',
40912     /**
40913      * @cfg {String} cls
40914      */   
40915     cls : '',
40916     /**
40917      * @cfg {String} href
40918      */   
40919     href : '',
40920     /**
40921      * @cfg {String} size (xs|sm|md|md-left|md-right|tall|wide)
40922      */   
40923     size : 'xs',
40924     
40925     /**
40926      * @cfg {String} placetitle (center|bottom)
40927      */   
40928     placetitle : '',
40929     
40930     /**
40931      * @cfg {Boolean} isFitContainer defalut true
40932      */   
40933     isFitContainer : true, 
40934     
40935     /**
40936      * @cfg {Boolean} preventDefault defalut false
40937      */   
40938     preventDefault : false, 
40939     
40940     /**
40941      * @cfg {Boolean} inverse defalut false
40942      */   
40943     maskInverse : false, 
40944     
40945     getAutoCreate : function()
40946     {
40947         if(!this.isFitContainer){
40948             return this.getSplitAutoCreate();
40949         }
40950         
40951         var cls = 'masonry-brick masonry-brick-full';
40952         
40953         if(this.href.length){
40954             cls += ' masonry-brick-link';
40955         }
40956         
40957         if(this.bgimage.length){
40958             cls += ' masonry-brick-image';
40959         }
40960         
40961         if(this.maskInverse){
40962             cls += ' mask-inverse';
40963         }
40964         
40965         if(!this.html.length && !this.maskInverse && !this.videourl.length){
40966             cls += ' enable-mask';
40967         }
40968         
40969         if(this.size){
40970             cls += ' masonry-' + this.size + '-brick';
40971         }
40972         
40973         if(this.placetitle.length){
40974             
40975             switch (this.placetitle) {
40976                 case 'center' :
40977                     cls += ' masonry-center-title';
40978                     break;
40979                 case 'bottom' :
40980                     cls += ' masonry-bottom-title';
40981                     break;
40982                 default:
40983                     break;
40984             }
40985             
40986         } else {
40987             if(!this.html.length && !this.bgimage.length){
40988                 cls += ' masonry-center-title';
40989             }
40990
40991             if(!this.html.length && this.bgimage.length){
40992                 cls += ' masonry-bottom-title';
40993             }
40994         }
40995         
40996         if(this.cls){
40997             cls += ' ' + this.cls;
40998         }
40999         
41000         var cfg = {
41001             tag: (this.href.length) ? 'a' : 'div',
41002             cls: cls,
41003             cn: [
41004                 {
41005                     tag: 'div',
41006                     cls: 'masonry-brick-mask'
41007                 },
41008                 {
41009                     tag: 'div',
41010                     cls: 'masonry-brick-paragraph',
41011                     cn: []
41012                 }
41013             ]
41014         };
41015         
41016         if(this.href.length){
41017             cfg.href = this.href;
41018         }
41019         
41020         var cn = cfg.cn[1].cn;
41021         
41022         if(this.title.length){
41023             cn.push({
41024                 tag: 'h4',
41025                 cls: 'masonry-brick-title',
41026                 html: this.title
41027             });
41028         }
41029         
41030         if(this.html.length){
41031             cn.push({
41032                 tag: 'p',
41033                 cls: 'masonry-brick-text',
41034                 html: this.html
41035             });
41036         }
41037         
41038         if (!this.title.length && !this.html.length) {
41039             cfg.cn[1].cls += ' hide';
41040         }
41041         
41042         if(this.bgimage.length){
41043             cfg.cn.push({
41044                 tag: 'img',
41045                 cls: 'masonry-brick-image-view',
41046                 src: this.bgimage
41047             });
41048         }
41049         
41050         if(this.videourl.length){
41051             var vurl = this.videourl.replace(/https:\/\/youtu\.be/, 'https://www.youtube.com/embed/');
41052             // youtube support only?
41053             cfg.cn.push({
41054                 tag: 'iframe',
41055                 cls: 'masonry-brick-image-view',
41056                 src: vurl,
41057                 frameborder : 0,
41058                 allowfullscreen : true
41059             });
41060         }
41061         
41062         return cfg;
41063         
41064     },
41065     
41066     getSplitAutoCreate : function()
41067     {
41068         var cls = 'masonry-brick masonry-brick-split';
41069         
41070         if(this.href.length){
41071             cls += ' masonry-brick-link';
41072         }
41073         
41074         if(this.bgimage.length){
41075             cls += ' masonry-brick-image';
41076         }
41077         
41078         if(this.size){
41079             cls += ' masonry-' + this.size + '-brick';
41080         }
41081         
41082         switch (this.placetitle) {
41083             case 'center' :
41084                 cls += ' masonry-center-title';
41085                 break;
41086             case 'bottom' :
41087                 cls += ' masonry-bottom-title';
41088                 break;
41089             default:
41090                 if(!this.bgimage.length){
41091                     cls += ' masonry-center-title';
41092                 }
41093
41094                 if(this.bgimage.length){
41095                     cls += ' masonry-bottom-title';
41096                 }
41097                 break;
41098         }
41099         
41100         if(this.cls){
41101             cls += ' ' + this.cls;
41102         }
41103         
41104         var cfg = {
41105             tag: (this.href.length) ? 'a' : 'div',
41106             cls: cls,
41107             cn: [
41108                 {
41109                     tag: 'div',
41110                     cls: 'masonry-brick-split-head',
41111                     cn: [
41112                         {
41113                             tag: 'div',
41114                             cls: 'masonry-brick-paragraph',
41115                             cn: []
41116                         }
41117                     ]
41118                 },
41119                 {
41120                     tag: 'div',
41121                     cls: 'masonry-brick-split-body',
41122                     cn: []
41123                 }
41124             ]
41125         };
41126         
41127         if(this.href.length){
41128             cfg.href = this.href;
41129         }
41130         
41131         if(this.title.length){
41132             cfg.cn[0].cn[0].cn.push({
41133                 tag: 'h4',
41134                 cls: 'masonry-brick-title',
41135                 html: this.title
41136             });
41137         }
41138         
41139         if(this.html.length){
41140             cfg.cn[1].cn.push({
41141                 tag: 'p',
41142                 cls: 'masonry-brick-text',
41143                 html: this.html
41144             });
41145         }
41146
41147         if(this.bgimage.length){
41148             cfg.cn[0].cn.push({
41149                 tag: 'img',
41150                 cls: 'masonry-brick-image-view',
41151                 src: this.bgimage
41152             });
41153         }
41154         
41155         if(this.videourl.length){
41156             var vurl = this.videourl.replace(/https:\/\/youtu\.be/, 'https://www.youtube.com/embed/');
41157             // youtube support only?
41158             cfg.cn[0].cn.cn.push({
41159                 tag: 'iframe',
41160                 cls: 'masonry-brick-image-view',
41161                 src: vurl,
41162                 frameborder : 0,
41163                 allowfullscreen : true
41164             });
41165         }
41166         
41167         return cfg;
41168     },
41169     
41170     initEvents: function() 
41171     {
41172         switch (this.size) {
41173             case 'xs' :
41174                 this.x = 1;
41175                 this.y = 1;
41176                 break;
41177             case 'sm' :
41178                 this.x = 2;
41179                 this.y = 2;
41180                 break;
41181             case 'md' :
41182             case 'md-left' :
41183             case 'md-right' :
41184                 this.x = 3;
41185                 this.y = 3;
41186                 break;
41187             case 'tall' :
41188                 this.x = 2;
41189                 this.y = 3;
41190                 break;
41191             case 'wide' :
41192                 this.x = 3;
41193                 this.y = 2;
41194                 break;
41195             case 'wide-thin' :
41196                 this.x = 3;
41197                 this.y = 1;
41198                 break;
41199                         
41200             default :
41201                 break;
41202         }
41203         
41204         if(Roo.isTouch){
41205             this.el.on('touchstart', this.onTouchStart, this);
41206             this.el.on('touchmove', this.onTouchMove, this);
41207             this.el.on('touchend', this.onTouchEnd, this);
41208             this.el.on('contextmenu', this.onContextMenu, this);
41209         } else {
41210             this.el.on('mouseenter'  ,this.enter, this);
41211             this.el.on('mouseleave', this.leave, this);
41212             this.el.on('click', this.onClick, this);
41213         }
41214         
41215         if (typeof(this.parent().bricks) == 'object' && this.parent().bricks != null) {
41216             this.parent().bricks.push(this);   
41217         }
41218         
41219     },
41220     
41221     onClick: function(e, el)
41222     {
41223         var time = this.endTimer - this.startTimer;
41224         // Roo.log(e.preventDefault());
41225         if(Roo.isTouch){
41226             if(time > 1000){
41227                 e.preventDefault();
41228                 return;
41229             }
41230         }
41231         
41232         if(!this.preventDefault){
41233             return;
41234         }
41235         
41236         e.preventDefault();
41237         
41238         if (this.activeClass != '') {
41239             this.selectBrick();
41240         }
41241         
41242         this.fireEvent('click', this, e);
41243     },
41244     
41245     enter: function(e, el)
41246     {
41247         e.preventDefault();
41248         
41249         if(!this.isFitContainer || this.maskInverse || this.videourl.length){
41250             return;
41251         }
41252         
41253         if(this.bgimage.length && this.html.length){
41254             this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
41255         }
41256     },
41257     
41258     leave: function(e, el)
41259     {
41260         e.preventDefault();
41261         
41262         if(!this.isFitContainer || this.maskInverse  || this.videourl.length){
41263             return;
41264         }
41265         
41266         if(this.bgimage.length && this.html.length){
41267             this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
41268         }
41269     },
41270     
41271     onTouchStart: function(e, el)
41272     {
41273 //        e.preventDefault();
41274         
41275         this.touchmoved = false;
41276         
41277         if(!this.isFitContainer){
41278             return;
41279         }
41280         
41281         if(!this.bgimage.length || !this.html.length){
41282             return;
41283         }
41284         
41285         this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
41286         
41287         this.timer = new Date().getTime();
41288         
41289     },
41290     
41291     onTouchMove: function(e, el)
41292     {
41293         this.touchmoved = true;
41294     },
41295     
41296     onContextMenu : function(e,el)
41297     {
41298         e.preventDefault();
41299         e.stopPropagation();
41300         return false;
41301     },
41302     
41303     onTouchEnd: function(e, el)
41304     {
41305 //        e.preventDefault();
41306         
41307         if((new Date().getTime() - this.timer > 1000) || !this.href.length || this.touchmoved){
41308         
41309             this.leave(e,el);
41310             
41311             return;
41312         }
41313         
41314         if(!this.bgimage.length || !this.html.length){
41315             
41316             if(this.href.length){
41317                 window.location.href = this.href;
41318             }
41319             
41320             return;
41321         }
41322         
41323         if(!this.isFitContainer){
41324             return;
41325         }
41326         
41327         this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
41328         
41329         window.location.href = this.href;
41330     },
41331     
41332     //selection on single brick only
41333     selectBrick : function() {
41334         
41335         if (!this.parentId) {
41336             return;
41337         }
41338         
41339         var m = Roo.bootstrap.LayoutMasonry.get(this.parentId);
41340         var index = m.selectedBrick.indexOf(this.id);
41341         
41342         if ( index > -1) {
41343             m.selectedBrick.splice(index,1);
41344             this.el.removeClass(this.activeClass);
41345             return;
41346         }
41347         
41348         for(var i = 0; i < m.selectedBrick.length; i++) {
41349             var b = Roo.bootstrap.MasonryBrick.get(m.selectedBrick[i]);
41350             b.el.removeClass(b.activeClass);
41351         }
41352         
41353         m.selectedBrick = [];
41354         
41355         m.selectedBrick.push(this.id);
41356         this.el.addClass(this.activeClass);
41357         return;
41358     },
41359     
41360     isSelected : function(){
41361         return this.el.hasClass(this.activeClass);
41362         
41363     }
41364 });
41365
41366 Roo.apply(Roo.bootstrap.MasonryBrick, {
41367     
41368     //groups: {},
41369     groups : new Roo.util.MixedCollection(false, function(o) { return o.el.id; }),
41370      /**
41371     * register a Masonry Brick
41372     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
41373     */
41374     
41375     register : function(brick)
41376     {
41377         //this.groups[brick.id] = brick;
41378         this.groups.add(brick.id, brick);
41379     },
41380     /**
41381     * fetch a  masonry brick based on the masonry brick ID
41382     * @param {string} the masonry brick to add
41383     * @returns {Roo.bootstrap.MasonryBrick} the masonry brick
41384     */
41385     
41386     get: function(brick_id) 
41387     {
41388         // if (typeof(this.groups[brick_id]) == 'undefined') {
41389         //     return false;
41390         // }
41391         // return this.groups[brick_id] ;
41392         
41393         if(this.groups.key(brick_id)) {
41394             return this.groups.key(brick_id);
41395         }
41396         
41397         return false;
41398     }
41399     
41400     
41401     
41402 });
41403
41404  /*
41405  * - LGPL
41406  *
41407  * element
41408  * 
41409  */
41410
41411 /**
41412  * @class Roo.bootstrap.Brick
41413  * @extends Roo.bootstrap.Component
41414  * Bootstrap Brick class
41415  * 
41416  * @constructor
41417  * Create a new Brick
41418  * @param {Object} config The config object
41419  */
41420
41421 Roo.bootstrap.Brick = function(config){
41422     Roo.bootstrap.Brick.superclass.constructor.call(this, config);
41423     
41424     this.addEvents({
41425         // raw events
41426         /**
41427          * @event click
41428          * When a Brick is click
41429          * @param {Roo.bootstrap.Brick} this
41430          * @param {Roo.EventObject} e
41431          */
41432         "click" : true
41433     });
41434 };
41435
41436 Roo.extend(Roo.bootstrap.Brick, Roo.bootstrap.Component,  {
41437     
41438     /**
41439      * @cfg {String} title
41440      */   
41441     title : '',
41442     /**
41443      * @cfg {String} html
41444      */   
41445     html : '',
41446     /**
41447      * @cfg {String} bgimage
41448      */   
41449     bgimage : '',
41450     /**
41451      * @cfg {String} cls
41452      */   
41453     cls : '',
41454     /**
41455      * @cfg {String} href
41456      */   
41457     href : '',
41458     /**
41459      * @cfg {String} video
41460      */   
41461     video : '',
41462     /**
41463      * @cfg {Boolean} square
41464      */   
41465     square : true,
41466     
41467     getAutoCreate : function()
41468     {
41469         var cls = 'roo-brick';
41470         
41471         if(this.href.length){
41472             cls += ' roo-brick-link';
41473         }
41474         
41475         if(this.bgimage.length){
41476             cls += ' roo-brick-image';
41477         }
41478         
41479         if(!this.html.length && !this.bgimage.length){
41480             cls += ' roo-brick-center-title';
41481         }
41482         
41483         if(!this.html.length && this.bgimage.length){
41484             cls += ' roo-brick-bottom-title';
41485         }
41486         
41487         if(this.cls){
41488             cls += ' ' + this.cls;
41489         }
41490         
41491         var cfg = {
41492             tag: (this.href.length) ? 'a' : 'div',
41493             cls: cls,
41494             cn: [
41495                 {
41496                     tag: 'div',
41497                     cls: 'roo-brick-paragraph',
41498                     cn: []
41499                 }
41500             ]
41501         };
41502         
41503         if(this.href.length){
41504             cfg.href = this.href;
41505         }
41506         
41507         var cn = cfg.cn[0].cn;
41508         
41509         if(this.title.length){
41510             cn.push({
41511                 tag: 'h4',
41512                 cls: 'roo-brick-title',
41513                 html: this.title
41514             });
41515         }
41516         
41517         if(this.html.length){
41518             cn.push({
41519                 tag: 'p',
41520                 cls: 'roo-brick-text',
41521                 html: this.html
41522             });
41523         } else {
41524             cn.cls += ' hide';
41525         }
41526         
41527         if(this.bgimage.length){
41528             cfg.cn.push({
41529                 tag: 'img',
41530                 cls: 'roo-brick-image-view',
41531                 src: this.bgimage
41532             });
41533         }
41534         
41535         return cfg;
41536     },
41537     
41538     initEvents: function() 
41539     {
41540         if(this.title.length || this.html.length){
41541             this.el.on('mouseenter'  ,this.enter, this);
41542             this.el.on('mouseleave', this.leave, this);
41543         }
41544         
41545         Roo.EventManager.onWindowResize(this.resize, this); 
41546         
41547         if(this.bgimage.length){
41548             this.imageEl = this.el.select('.roo-brick-image-view', true).first();
41549             this.imageEl.on('load', this.onImageLoad, this);
41550             return;
41551         }
41552         
41553         this.resize();
41554     },
41555     
41556     onImageLoad : function()
41557     {
41558         this.resize();
41559     },
41560     
41561     resize : function()
41562     {
41563         var paragraph = this.el.select('.roo-brick-paragraph', true).first();
41564         
41565         paragraph.setHeight(paragraph.getWidth() + paragraph.getPadding('tb'));
41566         
41567         if(this.bgimage.length){
41568             var image = this.el.select('.roo-brick-image-view', true).first();
41569             
41570             image.setWidth(paragraph.getWidth());
41571             
41572             if(this.square){
41573                 image.setHeight(paragraph.getWidth());
41574             }
41575             
41576             this.el.setHeight(image.getHeight());
41577             paragraph.setHeight(image.getHeight());
41578             
41579         }
41580         
41581     },
41582     
41583     enter: function(e, el)
41584     {
41585         e.preventDefault();
41586         
41587         if(this.bgimage.length){
41588             this.el.select('.roo-brick-paragraph', true).first().setOpacity(0.9, true);
41589             this.el.select('.roo-brick-image-view', true).first().setOpacity(0.1, true);
41590         }
41591     },
41592     
41593     leave: function(e, el)
41594     {
41595         e.preventDefault();
41596         
41597         if(this.bgimage.length){
41598             this.el.select('.roo-brick-paragraph', true).first().setOpacity(0, true);
41599             this.el.select('.roo-brick-image-view', true).first().setOpacity(1, true);
41600         }
41601     }
41602     
41603 });
41604
41605  
41606
41607  /*
41608  * - LGPL
41609  *
41610  * Number field 
41611  */
41612
41613 /**
41614  * @class Roo.bootstrap.form.NumberField
41615  * @extends Roo.bootstrap.form.Input
41616  * Bootstrap NumberField class
41617  * 
41618  * 
41619  * 
41620  * 
41621  * @constructor
41622  * Create a new NumberField
41623  * @param {Object} config The config object
41624  */
41625
41626 Roo.bootstrap.form.NumberField = function(config){
41627     Roo.bootstrap.form.NumberField.superclass.constructor.call(this, config);
41628 };
41629
41630 Roo.extend(Roo.bootstrap.form.NumberField, Roo.bootstrap.form.Input, {
41631     
41632     /**
41633      * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
41634      */
41635     allowDecimals : true,
41636     /**
41637      * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
41638      */
41639     decimalSeparator : ".",
41640     /**
41641      * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
41642      */
41643     decimalPrecision : 2,
41644     /**
41645      * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
41646      */
41647     allowNegative : true,
41648     
41649     /**
41650      * @cfg {Boolean} allowZero False to blank out if the user enters '0' (defaults to true)
41651      */
41652     allowZero: true,
41653     /**
41654      * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
41655      */
41656     minValue : Number.NEGATIVE_INFINITY,
41657     /**
41658      * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
41659      */
41660     maxValue : Number.MAX_VALUE,
41661     /**
41662      * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
41663      */
41664     minText : "The minimum value for this field is {0}",
41665     /**
41666      * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
41667      */
41668     maxText : "The maximum value for this field is {0}",
41669     /**
41670      * @cfg {String} nanText Error text to display if the value is not a valid number.  For example, this can happen
41671      * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
41672      */
41673     nanText : "{0} is not a valid number",
41674     /**
41675      * @cfg {String} thousandsDelimiter Symbol of thousandsDelimiter
41676      */
41677     thousandsDelimiter : false,
41678     /**
41679      * @cfg {String} valueAlign alignment of value
41680      */
41681     valueAlign : "left",
41682
41683     getAutoCreate : function()
41684     {
41685         var hiddenInput = {
41686             tag: 'input',
41687             type: 'hidden',
41688             id: Roo.id(),
41689             cls: 'hidden-number-input'
41690         };
41691         
41692         if (this.name) {
41693             hiddenInput.name = this.name;
41694         }
41695         
41696         this.name = '';
41697         
41698         var cfg = Roo.bootstrap.form.NumberField.superclass.getAutoCreate.call(this);
41699         
41700         this.name = hiddenInput.name;
41701         
41702         if(cfg.cn.length > 0) {
41703             cfg.cn.push(hiddenInput);
41704         }
41705         
41706         return cfg;
41707     },
41708
41709     // private
41710     initEvents : function()
41711     {   
41712         Roo.bootstrap.form.NumberField.superclass.initEvents.call(this);
41713         
41714         var allowed = "0123456789";
41715         
41716         if(this.allowDecimals){
41717             allowed += this.decimalSeparator;
41718         }
41719         
41720         if(this.allowNegative){
41721             allowed += "-";
41722         }
41723         
41724         if(this.thousandsDelimiter) {
41725             allowed += ",";
41726         }
41727         
41728         this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
41729         
41730         var keyPress = function(e){
41731             
41732             var k = e.getKey();
41733             
41734             var c = e.getCharCode();
41735             
41736             if(
41737                     (String.fromCharCode(c) == '.' || String.fromCharCode(c) == '-') &&
41738                     allowed.indexOf(String.fromCharCode(c)) === -1
41739             ){
41740                 e.stopEvent();
41741                 return;
41742             }
41743             
41744             if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
41745                 return;
41746             }
41747             
41748             if(allowed.indexOf(String.fromCharCode(c)) === -1){
41749                 e.stopEvent();
41750             }
41751         };
41752         
41753         this.el.on("keypress", keyPress, this);
41754     },
41755     
41756     validateValue : function(value)
41757     {
41758         
41759         if(!Roo.bootstrap.form.NumberField.superclass.validateValue.call(this, value)){
41760             return false;
41761         }
41762         
41763         var num = this.parseValue(value);
41764         
41765         if(isNaN(num)){
41766             this.markInvalid(String.format(this.nanText, value));
41767             return false;
41768         }
41769         
41770         if(num < this.minValue){
41771             this.markInvalid(String.format(this.minText, this.minValue));
41772             return false;
41773         }
41774         
41775         if(num > this.maxValue){
41776             this.markInvalid(String.format(this.maxText, this.maxValue));
41777             return false;
41778         }
41779         
41780         return true;
41781     },
41782
41783     getValue : function()
41784     {
41785         var v = this.hiddenEl().getValue();
41786         
41787         return this.fixPrecision(this.parseValue(v));
41788     },
41789
41790     parseValue : function(value)
41791     {
41792         if(this.thousandsDelimiter) {
41793             value += "";
41794             r = new RegExp(",", "g");
41795             value = value.replace(r, "");
41796         }
41797         
41798         value = parseFloat(String(value).replace(this.decimalSeparator, "."));
41799         return isNaN(value) ? '' : value;
41800     },
41801
41802     fixPrecision : function(value)
41803     {
41804         if(this.thousandsDelimiter) {
41805             value += "";
41806             r = new RegExp(",", "g");
41807             value = value.replace(r, "");
41808         }
41809         
41810         var nan = isNaN(value);
41811         
41812         if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
41813             return nan ? '' : value;
41814         }
41815         return parseFloat(value).toFixed(this.decimalPrecision);
41816     },
41817
41818     setValue : function(v)
41819     {
41820         v = String(this.fixPrecision(v)).replace(".", this.decimalSeparator);
41821         
41822         this.value = v;
41823         
41824         if(this.rendered){
41825             
41826             this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
41827             
41828             this.inputEl().dom.value = (v == '') ? '' :
41829                 Roo.util.Format.number(v, this.decimalPrecision, this.thousandsDelimiter || '');
41830             
41831             if(!this.allowZero && v === '0') {
41832                 this.hiddenEl().dom.value = '';
41833                 this.inputEl().dom.value = '';
41834             }
41835             
41836             this.validate();
41837         }
41838     },
41839
41840     decimalPrecisionFcn : function(v)
41841     {
41842         return Math.floor(v);
41843     },
41844
41845     beforeBlur : function()
41846     {
41847         var v = this.parseValue(this.getRawValue());
41848         
41849         if(v || v === 0 || v === ''){
41850             this.setValue(v);
41851         }
41852     },
41853     
41854     hiddenEl : function()
41855     {
41856         return this.el.select('input.hidden-number-input',true).first();
41857     }
41858     
41859 });
41860
41861  
41862
41863 /*
41864 * Licence: LGPL
41865 */
41866
41867 /**
41868  * @class Roo.bootstrap.DocumentSlider
41869  * @extends Roo.bootstrap.Component
41870  * Bootstrap DocumentSlider class
41871  * 
41872  * @constructor
41873  * Create a new DocumentViewer
41874  * @param {Object} config The config object
41875  */
41876
41877 Roo.bootstrap.DocumentSlider = function(config){
41878     Roo.bootstrap.DocumentSlider.superclass.constructor.call(this, config);
41879     
41880     this.files = [];
41881     
41882     this.addEvents({
41883         /**
41884          * @event initial
41885          * Fire after initEvent
41886          * @param {Roo.bootstrap.DocumentSlider} this
41887          */
41888         "initial" : true,
41889         /**
41890          * @event update
41891          * Fire after update
41892          * @param {Roo.bootstrap.DocumentSlider} this
41893          */
41894         "update" : true,
41895         /**
41896          * @event click
41897          * Fire after click
41898          * @param {Roo.bootstrap.DocumentSlider} this
41899          */
41900         "click" : true
41901     });
41902 };
41903
41904 Roo.extend(Roo.bootstrap.DocumentSlider, Roo.bootstrap.Component,  {
41905     
41906     files : false,
41907     
41908     indicator : 0,
41909     
41910     getAutoCreate : function()
41911     {
41912         var cfg = {
41913             tag : 'div',
41914             cls : 'roo-document-slider',
41915             cn : [
41916                 {
41917                     tag : 'div',
41918                     cls : 'roo-document-slider-header',
41919                     cn : [
41920                         {
41921                             tag : 'div',
41922                             cls : 'roo-document-slider-header-title'
41923                         }
41924                     ]
41925                 },
41926                 {
41927                     tag : 'div',
41928                     cls : 'roo-document-slider-body',
41929                     cn : [
41930                         {
41931                             tag : 'div',
41932                             cls : 'roo-document-slider-prev',
41933                             cn : [
41934                                 {
41935                                     tag : 'i',
41936                                     cls : 'fa fa-chevron-left'
41937                                 }
41938                             ]
41939                         },
41940                         {
41941                             tag : 'div',
41942                             cls : 'roo-document-slider-thumb',
41943                             cn : [
41944                                 {
41945                                     tag : 'img',
41946                                     cls : 'roo-document-slider-image'
41947                                 }
41948                             ]
41949                         },
41950                         {
41951                             tag : 'div',
41952                             cls : 'roo-document-slider-next',
41953                             cn : [
41954                                 {
41955                                     tag : 'i',
41956                                     cls : 'fa fa-chevron-right'
41957                                 }
41958                             ]
41959                         }
41960                     ]
41961                 }
41962             ]
41963         };
41964         
41965         return cfg;
41966     },
41967     
41968     initEvents : function()
41969     {
41970         this.headerEl = this.el.select('.roo-document-slider-header', true).first();
41971         this.headerEl.setVisibilityMode(Roo.Element.DISPLAY);
41972         
41973         this.titleEl = this.el.select('.roo-document-slider-header .roo-document-slider-header-title', true).first();
41974         this.titleEl.setVisibilityMode(Roo.Element.DISPLAY);
41975         
41976         this.bodyEl = this.el.select('.roo-document-slider-body', true).first();
41977         this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY);
41978         
41979         this.thumbEl = this.el.select('.roo-document-slider-thumb', true).first();
41980         this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY);
41981         
41982         this.imageEl = this.el.select('.roo-document-slider-image', true).first();
41983         this.imageEl.setVisibilityMode(Roo.Element.DISPLAY);
41984         
41985         this.prevIndicator = this.el.select('.roo-document-slider-prev i', true).first();
41986         this.prevIndicator.setVisibilityMode(Roo.Element.DISPLAY);
41987         
41988         this.nextIndicator = this.el.select('.roo-document-slider-next i', true).first();
41989         this.nextIndicator.setVisibilityMode(Roo.Element.DISPLAY);
41990         
41991         this.thumbEl.on('click', this.onClick, this);
41992         
41993         this.prevIndicator.on('click', this.prev, this);
41994         
41995         this.nextIndicator.on('click', this.next, this);
41996         
41997     },
41998     
41999     initial : function()
42000     {
42001         if(this.files.length){
42002             this.indicator = 1;
42003             this.update()
42004         }
42005         
42006         this.fireEvent('initial', this);
42007     },
42008     
42009     update : function()
42010     {
42011         this.imageEl.attr('src', this.files[this.indicator - 1]);
42012         
42013         this.titleEl.dom.innerHTML = String.format('{0} / {1}', this.indicator, this.files.length);
42014         
42015         this.prevIndicator.show();
42016         
42017         if(this.indicator == 1){
42018             this.prevIndicator.hide();
42019         }
42020         
42021         this.nextIndicator.show();
42022         
42023         if(this.indicator == this.files.length){
42024             this.nextIndicator.hide();
42025         }
42026         
42027         this.thumbEl.scrollTo('top');
42028         
42029         this.fireEvent('update', this);
42030     },
42031     
42032     onClick : function(e)
42033     {
42034         e.preventDefault();
42035         
42036         this.fireEvent('click', this);
42037     },
42038     
42039     prev : function(e)
42040     {
42041         e.preventDefault();
42042         
42043         this.indicator = Math.max(1, this.indicator - 1);
42044         
42045         this.update();
42046     },
42047     
42048     next : function(e)
42049     {
42050         e.preventDefault();
42051         
42052         this.indicator = Math.min(this.files.length, this.indicator + 1);
42053         
42054         this.update();
42055     }
42056 });
42057 /*
42058  * - LGPL
42059  *
42060  * RadioSet
42061  *
42062  *
42063  */
42064
42065 /**
42066  * @class Roo.bootstrap.form.RadioSet
42067  * @extends Roo.bootstrap.form.Input
42068  * @children Roo.bootstrap.form.Radio
42069  * Bootstrap RadioSet class
42070  * @cfg {String} indicatorpos (left|right) default left
42071  * @cfg {Boolean} inline (true|false) inline the element (default true)
42072  * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the radio
42073  * @constructor
42074  * Create a new RadioSet
42075  * @param {Object} config The config object
42076  */
42077
42078 Roo.bootstrap.form.RadioSet = function(config){
42079     
42080     Roo.bootstrap.form.RadioSet.superclass.constructor.call(this, config);
42081     
42082     this.radioes = [];
42083     
42084     Roo.bootstrap.form.RadioSet.register(this);
42085     
42086     this.addEvents({
42087         /**
42088         * @event check
42089         * Fires when the element is checked or unchecked.
42090         * @param {Roo.bootstrap.form.RadioSet} this This radio
42091         * @param {Roo.bootstrap.form.Radio} item The checked item
42092         */
42093        check : true,
42094        /**
42095         * @event click
42096         * Fires when the element is click.
42097         * @param {Roo.bootstrap.form.RadioSet} this This radio set
42098         * @param {Roo.bootstrap.form.Radio} item The checked item
42099         * @param {Roo.EventObject} e The event object
42100         */
42101        click : true
42102     });
42103     
42104 };
42105
42106 Roo.extend(Roo.bootstrap.form.RadioSet, Roo.bootstrap.form.Input,  {
42107
42108     radioes : false,
42109     
42110     inline : true,
42111     
42112     weight : '',
42113     
42114     indicatorpos : 'left',
42115     
42116     getAutoCreate : function()
42117     {
42118         var label = {
42119             tag : 'label',
42120             cls : 'roo-radio-set-label',
42121             cn : [
42122                 {
42123                     tag : 'span',
42124                     html : this.fieldLabel
42125                 }
42126             ]
42127         };
42128         if (Roo.bootstrap.version == 3) {
42129             
42130             
42131             if(this.indicatorpos == 'left'){
42132                 label.cn.unshift({
42133                     tag : 'i',
42134                     cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
42135                     tooltip : 'This field is required'
42136                 });
42137             } else {
42138                 label.cn.push({
42139                     tag : 'i',
42140                     cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
42141                     tooltip : 'This field is required'
42142                 });
42143             }
42144         }
42145         var items = {
42146             tag : 'div',
42147             cls : 'roo-radio-set-items'
42148         };
42149         
42150         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
42151         
42152         if (align === 'left' && this.fieldLabel.length) {
42153             
42154             items = {
42155                 cls : "roo-radio-set-right", 
42156                 cn: [
42157                     items
42158                 ]
42159             };
42160             
42161             if(this.labelWidth > 12){
42162                 label.style = "width: " + this.labelWidth + 'px';
42163             }
42164             
42165             if(this.labelWidth < 13 && this.labelmd == 0){
42166                 this.labelmd = this.labelWidth;
42167             }
42168             
42169             if(this.labellg > 0){
42170                 label.cls += ' col-lg-' + this.labellg;
42171                 items.cls += ' col-lg-' + (12 - this.labellg);
42172             }
42173             
42174             if(this.labelmd > 0){
42175                 label.cls += ' col-md-' + this.labelmd;
42176                 items.cls += ' col-md-' + (12 - this.labelmd);
42177             }
42178             
42179             if(this.labelsm > 0){
42180                 label.cls += ' col-sm-' + this.labelsm;
42181                 items.cls += ' col-sm-' + (12 - this.labelsm);
42182             }
42183             
42184             if(this.labelxs > 0){
42185                 label.cls += ' col-xs-' + this.labelxs;
42186                 items.cls += ' col-xs-' + (12 - this.labelxs);
42187             }
42188         }
42189         
42190         var cfg = {
42191             tag : 'div',
42192             cls : 'roo-radio-set',
42193             cn : [
42194                 {
42195                     tag : 'input',
42196                     cls : 'roo-radio-set-input',
42197                     type : 'hidden',
42198                     name : this.name,
42199                     value : this.value ? this.value :  ''
42200                 },
42201                 label,
42202                 items
42203             ]
42204         };
42205         
42206         if(this.weight.length){
42207             cfg.cls += ' roo-radio-' + this.weight;
42208         }
42209         
42210         if(this.inline) {
42211             cfg.cls += ' roo-radio-set-inline';
42212         }
42213         
42214         var settings=this;
42215         ['xs','sm','md','lg'].map(function(size){
42216             if (settings[size]) {
42217                 cfg.cls += ' col-' + size + '-' + settings[size];
42218             }
42219         });
42220         
42221         return cfg;
42222         
42223     },
42224
42225     initEvents : function()
42226     {
42227         this.labelEl = this.el.select('.roo-radio-set-label', true).first();
42228         this.labelEl.setVisibilityMode(Roo.Element.DISPLAY);
42229         
42230         if(!this.fieldLabel.length){
42231             this.labelEl.hide();
42232         }
42233         
42234         this.itemsEl = this.el.select('.roo-radio-set-items', true).first();
42235         this.itemsEl.setVisibilityMode(Roo.Element.DISPLAY);
42236         
42237         this.indicator = this.indicatorEl();
42238         
42239         if(this.indicator){
42240             this.indicator.addClass('invisible');
42241         }
42242         
42243         this.originalValue = this.getValue();
42244         
42245     },
42246     
42247     inputEl: function ()
42248     {
42249         return this.el.select('.roo-radio-set-input', true).first();
42250     },
42251     
42252     getChildContainer : function()
42253     {
42254         return this.itemsEl;
42255     },
42256     
42257     register : function(item)
42258     {
42259         this.radioes.push(item);
42260         
42261     },
42262     
42263     validate : function()
42264     {   
42265         if(this.getVisibilityEl().hasClass('hidden')){
42266             return true;
42267         }
42268         
42269         var valid = false;
42270         
42271         Roo.each(this.radioes, function(i){
42272             if(!i.checked){
42273                 return;
42274             }
42275             
42276             valid = true;
42277             return false;
42278         });
42279         
42280         if(this.allowBlank) {
42281             return true;
42282         }
42283         
42284         if(this.disabled || valid){
42285             this.markValid();
42286             return true;
42287         }
42288         
42289         this.markInvalid();
42290         return false;
42291         
42292     },
42293     
42294     markValid : function()
42295     {
42296         if(this.labelEl.isVisible(true) && this.indicatorEl()){
42297             this.indicatorEl().removeClass('visible');
42298             this.indicatorEl().addClass('invisible');
42299         }
42300         
42301         
42302         if (Roo.bootstrap.version == 3) {
42303             this.el.removeClass([this.invalidClass, this.validClass]);
42304             this.el.addClass(this.validClass);
42305         } else {
42306             this.el.removeClass(['is-invalid','is-valid']);
42307             this.el.addClass(['is-valid']);
42308         }
42309         this.fireEvent('valid', this);
42310     },
42311     
42312     markInvalid : function(msg)
42313     {
42314         if(this.allowBlank || this.disabled){
42315             return;
42316         }
42317         
42318         if(this.labelEl.isVisible(true) && this.indicatorEl()){
42319             this.indicatorEl().removeClass('invisible');
42320             this.indicatorEl().addClass('visible');
42321         }
42322         if (Roo.bootstrap.version == 3) {
42323             this.el.removeClass([this.invalidClass, this.validClass]);
42324             this.el.addClass(this.invalidClass);
42325         } else {
42326             this.el.removeClass(['is-invalid','is-valid']);
42327             this.el.addClass(['is-invalid']);
42328         }
42329         
42330         this.fireEvent('invalid', this, msg);
42331         
42332     },
42333     
42334     setValue : function(v, suppressEvent)
42335     {   
42336         if(this.value === v){
42337             return;
42338         }
42339         
42340         this.value = v;
42341         
42342         if(this.rendered){
42343             this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
42344         }
42345         
42346         Roo.each(this.radioes, function(i){
42347             i.checked = false;
42348             i.el.removeClass('checked');
42349         });
42350         
42351         Roo.each(this.radioes, function(i){
42352             
42353             if(i.value === v || i.value.toString() === v.toString()){
42354                 i.checked = true;
42355                 i.el.addClass('checked');
42356                 
42357                 if(suppressEvent !== true){
42358                     this.fireEvent('check', this, i);
42359                 }
42360                 
42361                 return false;
42362             }
42363             
42364         }, this);
42365         
42366         this.validate();
42367     },
42368     
42369     clearInvalid : function(){
42370         
42371         if(!this.el || this.preventMark){
42372             return;
42373         }
42374         
42375         this.el.removeClass([this.invalidClass]);
42376         
42377         this.fireEvent('valid', this);
42378     }
42379     
42380 });
42381
42382 Roo.apply(Roo.bootstrap.form.RadioSet, {
42383     
42384     groups: {},
42385     
42386     register : function(set)
42387     {
42388         this.groups[set.name] = set;
42389     },
42390     
42391     get: function(name) 
42392     {
42393         if (typeof(this.groups[name]) == 'undefined') {
42394             return false;
42395         }
42396         
42397         return this.groups[name] ;
42398     }
42399     
42400 });
42401 /*
42402  * Based on:
42403  * Ext JS Library 1.1.1
42404  * Copyright(c) 2006-2007, Ext JS, LLC.
42405  *
42406  * Originally Released Under LGPL - original licence link has changed is not relivant.
42407  *
42408  * Fork - LGPL
42409  * <script type="text/javascript">
42410  */
42411
42412
42413 /**
42414  * @class Roo.bootstrap.SplitBar
42415  * @extends Roo.util.Observable
42416  * Creates draggable splitter bar functionality from two elements (element to be dragged and element to be resized).
42417  * <br><br>
42418  * Usage:
42419  * <pre><code>
42420 var split = new Roo.bootstrap.SplitBar("elementToDrag", "elementToSize",
42421                    Roo.bootstrap.SplitBar.HORIZONTAL, Roo.bootstrap.SplitBar.LEFT);
42422 split.setAdapter(new Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter("container"));
42423 split.minSize = 100;
42424 split.maxSize = 600;
42425 split.animate = true;
42426 split.on('moved', splitterMoved);
42427 </code></pre>
42428  * @constructor
42429  * Create a new SplitBar
42430  * @config {String/HTMLElement/Roo.Element} dragElement The element to be dragged and act as the SplitBar. 
42431  * @config {String/HTMLElement/Roo.Element} resizingElement The element to be resized based on where the SplitBar element is dragged 
42432  * @config {Number} orientation (optional) Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
42433  * @config {Number} placement (optional) Either Roo.bootstrap.SplitBar.LEFT or Roo.bootstrap.SplitBar.RIGHT for horizontal or  
42434                         Roo.bootstrap.SplitBar.TOP or Roo.bootstrap.SplitBar.BOTTOM for vertical. (By default, this is determined automatically by the initial
42435                         position of the SplitBar).
42436  */
42437 Roo.bootstrap.SplitBar = function(cfg){
42438     
42439     /** @private */
42440     
42441     //{
42442     //  dragElement : elm
42443     //  resizingElement: el,
42444         // optional..
42445     //    orientation : Either Roo.bootstrap.SplitBar.HORIZONTAL
42446     //    placement : Roo.bootstrap.SplitBar.LEFT  ,
42447         // existingProxy ???
42448     //}
42449     
42450     this.el = Roo.get(cfg.dragElement, true);
42451     this.el.dom.unselectable = "on";
42452     /** @private */
42453     this.resizingEl = Roo.get(cfg.resizingElement, true);
42454
42455     /**
42456      * @private
42457      * The orientation of the split. Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
42458      * Note: If this is changed after creating the SplitBar, the placement property must be manually updated
42459      * @type Number
42460      */
42461     this.orientation = cfg.orientation || Roo.bootstrap.SplitBar.HORIZONTAL;
42462     
42463     /**
42464      * The minimum size of the resizing element. (Defaults to 0)
42465      * @type Number
42466      */
42467     this.minSize = 0;
42468     
42469     /**
42470      * The maximum size of the resizing element. (Defaults to 2000)
42471      * @type Number
42472      */
42473     this.maxSize = 2000;
42474     
42475     /**
42476      * Whether to animate the transition to the new size
42477      * @type Boolean
42478      */
42479     this.animate = false;
42480     
42481     /**
42482      * Whether to create a transparent shim that overlays the page when dragging, enables dragging across iframes.
42483      * @type Boolean
42484      */
42485     this.useShim = false;
42486     
42487     /** @private */
42488     this.shim = null;
42489     
42490     if(!cfg.existingProxy){
42491         /** @private */
42492         this.proxy = Roo.bootstrap.SplitBar.createProxy(this.orientation);
42493     }else{
42494         this.proxy = Roo.get(cfg.existingProxy).dom;
42495     }
42496     /** @private */
42497     this.dd = new Roo.dd.DDProxy(this.el.dom.id, "XSplitBars", {dragElId : this.proxy.id});
42498     
42499     /** @private */
42500     this.dd.b4StartDrag = this.onStartProxyDrag.createDelegate(this);
42501     
42502     /** @private */
42503     this.dd.endDrag = this.onEndProxyDrag.createDelegate(this);
42504     
42505     /** @private */
42506     this.dragSpecs = {};
42507     
42508     /**
42509      * @private The adapter to use to positon and resize elements
42510      */
42511     this.adapter = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
42512     this.adapter.init(this);
42513     
42514     if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
42515         /** @private */
42516         this.placement = cfg.placement || (this.el.getX() > this.resizingEl.getX() ? Roo.bootstrap.SplitBar.LEFT : Roo.bootstrap.SplitBar.RIGHT);
42517         this.el.addClass("roo-splitbar-h");
42518     }else{
42519         /** @private */
42520         this.placement = cfg.placement || (this.el.getY() > this.resizingEl.getY() ? Roo.bootstrap.SplitBar.TOP : Roo.bootstrap.SplitBar.BOTTOM);
42521         this.el.addClass("roo-splitbar-v");
42522     }
42523     
42524     this.addEvents({
42525         /**
42526          * @event resize
42527          * Fires when the splitter is moved (alias for {@link #event-moved})
42528          * @param {Roo.bootstrap.SplitBar} this
42529          * @param {Number} newSize the new width or height
42530          */
42531         "resize" : true,
42532         /**
42533          * @event moved
42534          * Fires when the splitter is moved
42535          * @param {Roo.bootstrap.SplitBar} this
42536          * @param {Number} newSize the new width or height
42537          */
42538         "moved" : true,
42539         /**
42540          * @event beforeresize
42541          * Fires before the splitter is dragged
42542          * @param {Roo.bootstrap.SplitBar} this
42543          */
42544         "beforeresize" : true,
42545
42546         "beforeapply" : true
42547     });
42548
42549     Roo.util.Observable.call(this);
42550 };
42551
42552 Roo.extend(Roo.bootstrap.SplitBar, Roo.util.Observable, {
42553     onStartProxyDrag : function(x, y){
42554         this.fireEvent("beforeresize", this);
42555         if(!this.overlay){
42556             var o = Roo.DomHelper.insertFirst(document.body,  {cls: "roo-drag-overlay", html: "&#160;"}, true);
42557             o.unselectable();
42558             o.enableDisplayMode("block");
42559             // all splitbars share the same overlay
42560             Roo.bootstrap.SplitBar.prototype.overlay = o;
42561         }
42562         this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
42563         this.overlay.show();
42564         Roo.get(this.proxy).setDisplayed("block");
42565         var size = this.adapter.getElementSize(this);
42566         this.activeMinSize = this.getMinimumSize();;
42567         this.activeMaxSize = this.getMaximumSize();;
42568         var c1 = size - this.activeMinSize;
42569         var c2 = Math.max(this.activeMaxSize - size, 0);
42570         if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
42571             this.dd.resetConstraints();
42572             this.dd.setXConstraint(
42573                 this.placement == Roo.bootstrap.SplitBar.LEFT ? c1 : c2, 
42574                 this.placement == Roo.bootstrap.SplitBar.LEFT ? c2 : c1
42575             );
42576             this.dd.setYConstraint(0, 0);
42577         }else{
42578             this.dd.resetConstraints();
42579             this.dd.setXConstraint(0, 0);
42580             this.dd.setYConstraint(
42581                 this.placement == Roo.bootstrap.SplitBar.TOP ? c1 : c2, 
42582                 this.placement == Roo.bootstrap.SplitBar.TOP ? c2 : c1
42583             );
42584          }
42585         this.dragSpecs.startSize = size;
42586         this.dragSpecs.startPoint = [x, y];
42587         Roo.dd.DDProxy.prototype.b4StartDrag.call(this.dd, x, y);
42588     },
42589     
42590     /** 
42591      * @private Called after the drag operation by the DDProxy
42592      */
42593     onEndProxyDrag : function(e){
42594         Roo.get(this.proxy).setDisplayed(false);
42595         var endPoint = Roo.lib.Event.getXY(e);
42596         if(this.overlay){
42597             this.overlay.hide();
42598         }
42599         var newSize;
42600         if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
42601             newSize = this.dragSpecs.startSize + 
42602                 (this.placement == Roo.bootstrap.SplitBar.LEFT ?
42603                     endPoint[0] - this.dragSpecs.startPoint[0] :
42604                     this.dragSpecs.startPoint[0] - endPoint[0]
42605                 );
42606         }else{
42607             newSize = this.dragSpecs.startSize + 
42608                 (this.placement == Roo.bootstrap.SplitBar.TOP ?
42609                     endPoint[1] - this.dragSpecs.startPoint[1] :
42610                     this.dragSpecs.startPoint[1] - endPoint[1]
42611                 );
42612         }
42613         newSize = Math.min(Math.max(newSize, this.activeMinSize), this.activeMaxSize);
42614         if(newSize != this.dragSpecs.startSize){
42615             if(this.fireEvent('beforeapply', this, newSize) !== false){
42616                 this.adapter.setElementSize(this, newSize);
42617                 this.fireEvent("moved", this, newSize);
42618                 this.fireEvent("resize", this, newSize);
42619             }
42620         }
42621     },
42622     
42623     /**
42624      * Get the adapter this SplitBar uses
42625      * @return The adapter object
42626      */
42627     getAdapter : function(){
42628         return this.adapter;
42629     },
42630     
42631     /**
42632      * Set the adapter this SplitBar uses
42633      * @param {Object} adapter A SplitBar adapter object
42634      */
42635     setAdapter : function(adapter){
42636         this.adapter = adapter;
42637         this.adapter.init(this);
42638     },
42639     
42640     /**
42641      * Gets the minimum size for the resizing element
42642      * @return {Number} The minimum size
42643      */
42644     getMinimumSize : function(){
42645         return this.minSize;
42646     },
42647     
42648     /**
42649      * Sets the minimum size for the resizing element
42650      * @param {Number} minSize The minimum size
42651      */
42652     setMinimumSize : function(minSize){
42653         this.minSize = minSize;
42654     },
42655     
42656     /**
42657      * Gets the maximum size for the resizing element
42658      * @return {Number} The maximum size
42659      */
42660     getMaximumSize : function(){
42661         return this.maxSize;
42662     },
42663     
42664     /**
42665      * Sets the maximum size for the resizing element
42666      * @param {Number} maxSize The maximum size
42667      */
42668     setMaximumSize : function(maxSize){
42669         this.maxSize = maxSize;
42670     },
42671     
42672     /**
42673      * Sets the initialize size for the resizing element
42674      * @param {Number} size The initial size
42675      */
42676     setCurrentSize : function(size){
42677         var oldAnimate = this.animate;
42678         this.animate = false;
42679         this.adapter.setElementSize(this, size);
42680         this.animate = oldAnimate;
42681     },
42682     
42683     /**
42684      * Destroy this splitbar. 
42685      * @param {Boolean} removeEl True to remove the element
42686      */
42687     destroy : function(removeEl){
42688         if(this.shim){
42689             this.shim.remove();
42690         }
42691         this.dd.unreg();
42692         this.proxy.parentNode.removeChild(this.proxy);
42693         if(removeEl){
42694             this.el.remove();
42695         }
42696     }
42697 });
42698
42699 /**
42700  * @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.
42701  */
42702 Roo.bootstrap.SplitBar.createProxy = function(dir){
42703     var proxy = new Roo.Element(document.createElement("div"));
42704     proxy.unselectable();
42705     var cls = 'roo-splitbar-proxy';
42706     proxy.addClass(cls + ' ' + (dir == Roo.bootstrap.SplitBar.HORIZONTAL ? cls +'-h' : cls + '-v'));
42707     document.body.appendChild(proxy.dom);
42708     return proxy.dom;
42709 };
42710
42711 /** 
42712  * @class Roo.bootstrap.SplitBar.BasicLayoutAdapter
42713  * Default Adapter. It assumes the splitter and resizing element are not positioned
42714  * elements and only gets/sets the width of the element. Generally used for table based layouts.
42715  */
42716 Roo.bootstrap.SplitBar.BasicLayoutAdapter = function(){
42717 };
42718
42719 Roo.bootstrap.SplitBar.BasicLayoutAdapter.prototype = {
42720     // do nothing for now
42721     init : function(s){
42722     
42723     },
42724     /**
42725      * Called before drag operations to get the current size of the resizing element. 
42726      * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
42727      */
42728      getElementSize : function(s){
42729         if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
42730             return s.resizingEl.getWidth();
42731         }else{
42732             return s.resizingEl.getHeight();
42733         }
42734     },
42735     
42736     /**
42737      * Called after drag operations to set the size of the resizing element.
42738      * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
42739      * @param {Number} newSize The new size to set
42740      * @param {Function} onComplete A function to be invoked when resizing is complete
42741      */
42742     setElementSize : function(s, newSize, onComplete){
42743         if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
42744             if(!s.animate){
42745                 s.resizingEl.setWidth(newSize);
42746                 if(onComplete){
42747                     onComplete(s, newSize);
42748                 }
42749             }else{
42750                 s.resizingEl.setWidth(newSize, true, .1, onComplete, 'easeOut');
42751             }
42752         }else{
42753             
42754             if(!s.animate){
42755                 s.resizingEl.setHeight(newSize);
42756                 if(onComplete){
42757                     onComplete(s, newSize);
42758                 }
42759             }else{
42760                 s.resizingEl.setHeight(newSize, true, .1, onComplete, 'easeOut');
42761             }
42762         }
42763     }
42764 };
42765
42766 /** 
42767  *@class Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter
42768  * @extends Roo.bootstrap.SplitBar.BasicLayoutAdapter
42769  * Adapter that  moves the splitter element to align with the resized sizing element. 
42770  * Used with an absolute positioned SplitBar.
42771  * @param {String/HTMLElement/Roo.Element} container The container that wraps around the absolute positioned content. If it's
42772  * document.body, make sure you assign an id to the body element.
42773  */
42774 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter = function(container){
42775     this.basic = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
42776     this.container = Roo.get(container);
42777 };
42778
42779 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter.prototype = {
42780     init : function(s){
42781         this.basic.init(s);
42782     },
42783     
42784     getElementSize : function(s){
42785         return this.basic.getElementSize(s);
42786     },
42787     
42788     setElementSize : function(s, newSize, onComplete){
42789         this.basic.setElementSize(s, newSize, this.moveSplitter.createDelegate(this, [s]));
42790     },
42791     
42792     moveSplitter : function(s){
42793         var yes = Roo.bootstrap.SplitBar;
42794         switch(s.placement){
42795             case yes.LEFT:
42796                 s.el.setX(s.resizingEl.getRight());
42797                 break;
42798             case yes.RIGHT:
42799                 s.el.setStyle("right", (this.container.getWidth() - s.resizingEl.getLeft()) + "px");
42800                 break;
42801             case yes.TOP:
42802                 s.el.setY(s.resizingEl.getBottom());
42803                 break;
42804             case yes.BOTTOM:
42805                 s.el.setY(s.resizingEl.getTop() - s.el.getHeight());
42806                 break;
42807         }
42808     }
42809 };
42810
42811 /**
42812  * Orientation constant - Create a vertical SplitBar
42813  * @static
42814  * @type Number
42815  */
42816 Roo.bootstrap.SplitBar.VERTICAL = 1;
42817
42818 /**
42819  * Orientation constant - Create a horizontal SplitBar
42820  * @static
42821  * @type Number
42822  */
42823 Roo.bootstrap.SplitBar.HORIZONTAL = 2;
42824
42825 /**
42826  * Placement constant - The resizing element is to the left of the splitter element
42827  * @static
42828  * @type Number
42829  */
42830 Roo.bootstrap.SplitBar.LEFT = 1;
42831
42832 /**
42833  * Placement constant - The resizing element is to the right of the splitter element
42834  * @static
42835  * @type Number
42836  */
42837 Roo.bootstrap.SplitBar.RIGHT = 2;
42838
42839 /**
42840  * Placement constant - The resizing element is positioned above the splitter element
42841  * @static
42842  * @type Number
42843  */
42844 Roo.bootstrap.SplitBar.TOP = 3;
42845
42846 /**
42847  * Placement constant - The resizing element is positioned under splitter element
42848  * @static
42849  * @type Number
42850  */
42851 Roo.bootstrap.SplitBar.BOTTOM = 4;
42852 /*
42853  * Based on:
42854  * Ext JS Library 1.1.1
42855  * Copyright(c) 2006-2007, Ext JS, LLC.
42856  *
42857  * Originally Released Under LGPL - original licence link has changed is not relivant.
42858  *
42859  * Fork - LGPL
42860  * <script type="text/javascript">
42861  */
42862
42863 /**
42864  * @class Roo.bootstrap.layout.Manager
42865  * @extends Roo.bootstrap.Component
42866  * @abstract
42867  * Base class for layout managers.
42868  */
42869 Roo.bootstrap.layout.Manager = function(config)
42870 {
42871     this.monitorWindowResize = true; // do this before we apply configuration.
42872     
42873     Roo.bootstrap.layout.Manager.superclass.constructor.call(this,config);
42874
42875
42876
42877
42878
42879     /** false to disable window resize monitoring @type Boolean */
42880     
42881     this.regions = {};
42882     this.addEvents({
42883         /**
42884          * @event layout
42885          * Fires when a layout is performed.
42886          * @param {Roo.LayoutManager} this
42887          */
42888         "layout" : true,
42889         /**
42890          * @event regionresized
42891          * Fires when the user resizes a region.
42892          * @param {Roo.LayoutRegion} region The resized region
42893          * @param {Number} newSize The new size (width for east/west, height for north/south)
42894          */
42895         "regionresized" : true,
42896         /**
42897          * @event regioncollapsed
42898          * Fires when a region is collapsed.
42899          * @param {Roo.LayoutRegion} region The collapsed region
42900          */
42901         "regioncollapsed" : true,
42902         /**
42903          * @event regionexpanded
42904          * Fires when a region is expanded.
42905          * @param {Roo.LayoutRegion} region The expanded region
42906          */
42907         "regionexpanded" : true
42908     });
42909     this.updating = false;
42910
42911     if (config.el) {
42912         this.el = Roo.get(config.el);
42913         this.initEvents();
42914     }
42915
42916 };
42917
42918 Roo.extend(Roo.bootstrap.layout.Manager, Roo.bootstrap.Component, {
42919
42920
42921     regions : null,
42922
42923     monitorWindowResize : true,
42924
42925
42926     updating : false,
42927
42928
42929     onRender : function(ct, position)
42930     {
42931         if(!this.el){
42932             this.el = Roo.get(ct);
42933             this.initEvents();
42934         }
42935         //this.fireEvent('render',this);
42936     },
42937
42938
42939     initEvents: function()
42940     {
42941
42942
42943         // ie scrollbar fix
42944         if(this.el.dom == document.body && Roo.isIE && !config.allowScroll){
42945             document.body.scroll = "no";
42946         }else if(this.el.dom != document.body && this.el.getStyle('position') == 'static'){
42947             this.el.position('relative');
42948         }
42949         this.id = this.el.id;
42950         this.el.addClass("roo-layout-container");
42951         Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
42952         if(this.el.dom != document.body ) {
42953             this.el.on('resize', this.layout,this);
42954             this.el.on('show', this.layout,this);
42955         }
42956
42957     },
42958
42959     /**
42960      * Returns true if this layout is currently being updated
42961      * @return {Boolean}
42962      */
42963     isUpdating : function(){
42964         return this.updating;
42965     },
42966
42967     /**
42968      * Suspend the LayoutManager from doing auto-layouts while
42969      * making multiple add or remove calls
42970      */
42971     beginUpdate : function(){
42972         this.updating = true;
42973     },
42974
42975     /**
42976      * Restore auto-layouts and optionally disable the manager from performing a layout
42977      * @param {Boolean} noLayout true to disable a layout update
42978      */
42979     endUpdate : function(noLayout){
42980         this.updating = false;
42981         if(!noLayout){
42982             this.layout();
42983         }
42984     },
42985
42986     layout: function(){
42987         // abstract...
42988     },
42989
42990     onRegionResized : function(region, newSize){
42991         this.fireEvent("regionresized", region, newSize);
42992         this.layout();
42993     },
42994
42995     onRegionCollapsed : function(region){
42996         this.fireEvent("regioncollapsed", region);
42997     },
42998
42999     onRegionExpanded : function(region){
43000         this.fireEvent("regionexpanded", region);
43001     },
43002
43003     /**
43004      * Returns the size of the current view. This method normalizes document.body and element embedded layouts and
43005      * performs box-model adjustments.
43006      * @return {Object} The size as an object {width: (the width), height: (the height)}
43007      */
43008     getViewSize : function()
43009     {
43010         var size;
43011         if(this.el.dom != document.body){
43012             size = this.el.getSize();
43013         }else{
43014             size = {width: Roo.lib.Dom.getViewWidth(), height: Roo.lib.Dom.getViewHeight()};
43015         }
43016         size.width -= this.el.getBorderWidth("lr")-this.el.getPadding("lr");
43017         size.height -= this.el.getBorderWidth("tb")-this.el.getPadding("tb");
43018         return size;
43019     },
43020
43021     /**
43022      * Returns the Element this layout is bound to.
43023      * @return {Roo.Element}
43024      */
43025     getEl : function(){
43026         return this.el;
43027     },
43028
43029     /**
43030      * Returns the specified region.
43031      * @param {String} target The region key ('center', 'north', 'south', 'east' or 'west')
43032      * @return {Roo.LayoutRegion}
43033      */
43034     getRegion : function(target){
43035         return this.regions[target.toLowerCase()];
43036     },
43037
43038     onWindowResize : function(){
43039         if(this.monitorWindowResize){
43040             this.layout();
43041         }
43042     }
43043 });
43044 /*
43045  * Based on:
43046  * Ext JS Library 1.1.1
43047  * Copyright(c) 2006-2007, Ext JS, LLC.
43048  *
43049  * Originally Released Under LGPL - original licence link has changed is not relivant.
43050  *
43051  * Fork - LGPL
43052  * <script type="text/javascript">
43053  */
43054 /**
43055  * @class Roo.bootstrap.layout.Border
43056  * @extends Roo.bootstrap.layout.Manager
43057  * @children Roo.bootstrap.panel.Content Roo.bootstrap.panel.Nest Roo.bootstrap.panel.Grid
43058  * @parent builder Roo.bootstrap.panel.Nest Roo.bootstrap.panel.Nest Roo.bootstrap.Modal
43059  * This class represents a common layout manager used in desktop applications. For screenshots and more details,
43060  * please see: examples/bootstrap/nested.html<br><br>
43061  
43062 <b>The container the layout is rendered into can be either the body element or any other element.
43063 If it is not the body element, the container needs to either be an absolute positioned element,
43064 or you will need to add "position:relative" to the css of the container.  You will also need to specify
43065 the container size if it is not the body element.</b>
43066
43067 * @constructor
43068 * Create a new Border
43069 * @param {Object} config Configuration options
43070  */
43071 Roo.bootstrap.layout.Border = function(config){
43072     config = config || {};
43073     Roo.bootstrap.layout.Border.superclass.constructor.call(this, config);
43074     
43075     
43076     
43077     Roo.each(Roo.bootstrap.layout.Border.regions, function(region) {
43078         if(config[region]){
43079             config[region].region = region;
43080             this.addRegion(config[region]);
43081         }
43082     },this);
43083     
43084 };
43085
43086 Roo.bootstrap.layout.Border.regions =  ["center", "north","south","east","west"];
43087
43088 Roo.extend(Roo.bootstrap.layout.Border, Roo.bootstrap.layout.Manager, {
43089     
43090         /**
43091          * @cfg {Roo.bootstrap.layout.Region} center region to go in center
43092          */
43093         /**
43094          * @cfg {Roo.bootstrap.layout.Region} west region to go in west
43095          */
43096         /**
43097          * @cfg {Roo.bootstrap.layout.Region} east region to go in east
43098          */
43099         /**
43100          * @cfg {Roo.bootstrap.layout.Region} south region to go in south
43101          */
43102         /**
43103          * @cfg {Roo.bootstrap.layout.Region} north region to go in north
43104          */
43105         
43106         
43107         
43108         
43109     parent : false, // this might point to a 'nest' or a ???
43110     
43111     /**
43112      * Creates and adds a new region if it doesn't already exist.
43113      * @param {String} target The target region key (north, south, east, west or center).
43114      * @param {Object} config The regions config object
43115      * @return {BorderLayoutRegion} The new region
43116      */
43117     addRegion : function(config)
43118     {
43119         if(!this.regions[config.region]){
43120             var r = this.factory(config);
43121             this.bindRegion(r);
43122         }
43123         return this.regions[config.region];
43124     },
43125
43126     // private (kinda)
43127     bindRegion : function(r){
43128         this.regions[r.config.region] = r;
43129         
43130         r.on("visibilitychange",    this.layout, this);
43131         r.on("paneladded",          this.layout, this);
43132         r.on("panelremoved",        this.layout, this);
43133         r.on("invalidated",         this.layout, this);
43134         r.on("resized",             this.onRegionResized, this);
43135         r.on("collapsed",           this.onRegionCollapsed, this);
43136         r.on("expanded",            this.onRegionExpanded, this);
43137     },
43138
43139     /**
43140      * Performs a layout update.
43141      */
43142     layout : function()
43143     {
43144         if(this.updating) {
43145             return;
43146         }
43147         
43148         // render all the rebions if they have not been done alreayd?
43149         Roo.each(Roo.bootstrap.layout.Border.regions, function(region) {
43150             if(this.regions[region] && !this.regions[region].bodyEl){
43151                 this.regions[region].onRender(this.el)
43152             }
43153         },this);
43154         
43155         var size = this.getViewSize();
43156         var w = size.width;
43157         var h = size.height;
43158         var centerW = w;
43159         var centerH = h;
43160         var centerY = 0;
43161         var centerX = 0;
43162         //var x = 0, y = 0;
43163
43164         var rs = this.regions;
43165         var north = rs["north"];
43166         var south = rs["south"]; 
43167         var west = rs["west"];
43168         var east = rs["east"];
43169         var center = rs["center"];
43170         //if(this.hideOnLayout){ // not supported anymore
43171             //c.el.setStyle("display", "none");
43172         //}
43173         if(north && north.isVisible()){
43174             var b = north.getBox();
43175             var m = north.getMargins();
43176             b.width = w - (m.left+m.right);
43177             b.x = m.left;
43178             b.y = m.top;
43179             centerY = b.height + b.y + m.bottom;
43180             centerH -= centerY;
43181             north.updateBox(this.safeBox(b));
43182         }
43183         if(south && south.isVisible()){
43184             var b = south.getBox();
43185             var m = south.getMargins();
43186             b.width = w - (m.left+m.right);
43187             b.x = m.left;
43188             var totalHeight = (b.height + m.top + m.bottom);
43189             b.y = h - totalHeight + m.top;
43190             centerH -= totalHeight;
43191             south.updateBox(this.safeBox(b));
43192         }
43193         if(west && west.isVisible()){
43194             var b = west.getBox();
43195             var m = west.getMargins();
43196             b.height = centerH - (m.top+m.bottom);
43197             b.x = m.left;
43198             b.y = centerY + m.top;
43199             var totalWidth = (b.width + m.left + m.right);
43200             centerX += totalWidth;
43201             centerW -= totalWidth;
43202             west.updateBox(this.safeBox(b));
43203         }
43204         if(east && east.isVisible()){
43205             var b = east.getBox();
43206             var m = east.getMargins();
43207             b.height = centerH - (m.top+m.bottom);
43208             var totalWidth = (b.width + m.left + m.right);
43209             b.x = w - totalWidth + m.left;
43210             b.y = centerY + m.top;
43211             centerW -= totalWidth;
43212             east.updateBox(this.safeBox(b));
43213         }
43214         if(center){
43215             var m = center.getMargins();
43216             var centerBox = {
43217                 x: centerX + m.left,
43218                 y: centerY + m.top,
43219                 width: centerW - (m.left+m.right),
43220                 height: centerH - (m.top+m.bottom)
43221             };
43222             //if(this.hideOnLayout){
43223                 //center.el.setStyle("display", "block");
43224             //}
43225             center.updateBox(this.safeBox(centerBox));
43226         }
43227         this.el.repaint();
43228         this.fireEvent("layout", this);
43229     },
43230
43231     // private
43232     safeBox : function(box){
43233         box.width = Math.max(0, box.width);
43234         box.height = Math.max(0, box.height);
43235         return box;
43236     },
43237
43238     /**
43239      * Adds a ContentPanel (or subclass) to this layout.
43240      * @param {String} target The target region key (north, south, east, west or center).
43241      * @param {Roo.ContentPanel} panel The panel to add
43242      * @return {Roo.ContentPanel} The added panel
43243      */
43244     add : function(target, panel){
43245          
43246         target = target.toLowerCase();
43247         return this.regions[target].add(panel);
43248     },
43249
43250     /**
43251      * Remove a ContentPanel (or subclass) to this layout.
43252      * @param {String} target The target region key (north, south, east, west or center).
43253      * @param {Number/String/Roo.ContentPanel} panel The index, id or panel to remove
43254      * @return {Roo.ContentPanel} The removed panel
43255      */
43256     remove : function(target, panel){
43257         target = target.toLowerCase();
43258         return this.regions[target].remove(panel);
43259     },
43260
43261     /**
43262      * Searches all regions for a panel with the specified id
43263      * @param {String} panelId
43264      * @return {Roo.ContentPanel} The panel or null if it wasn't found
43265      */
43266     findPanel : function(panelId){
43267         var rs = this.regions;
43268         for(var target in rs){
43269             if(typeof rs[target] != "function"){
43270                 var p = rs[target].getPanel(panelId);
43271                 if(p){
43272                     return p;
43273                 }
43274             }
43275         }
43276         return null;
43277     },
43278
43279     /**
43280      * Searches all regions for a panel with the specified id and activates (shows) it.
43281      * @param {String/ContentPanel} panelId The panels id or the panel itself
43282      * @return {Roo.ContentPanel} The shown panel or null
43283      */
43284     showPanel : function(panelId) {
43285       var rs = this.regions;
43286       for(var target in rs){
43287          var r = rs[target];
43288          if(typeof r != "function"){
43289             if(r.hasPanel(panelId)){
43290                return r.showPanel(panelId);
43291             }
43292          }
43293       }
43294       return null;
43295    },
43296
43297    /**
43298      * Restores this layout's state using Roo.state.Manager or the state provided by the passed provider.
43299      * @param {Roo.state.Provider} provider (optional) An alternate state provider
43300      */
43301    /*
43302     restoreState : function(provider){
43303         if(!provider){
43304             provider = Roo.state.Manager;
43305         }
43306         var sm = new Roo.LayoutStateManager();
43307         sm.init(this, provider);
43308     },
43309 */
43310  
43311  
43312     /**
43313      * Adds a xtype elements to the layout.
43314      * <pre><code>
43315
43316 layout.addxtype({
43317        xtype : 'ContentPanel',
43318        region: 'west',
43319        items: [ .... ]
43320    }
43321 );
43322
43323 layout.addxtype({
43324         xtype : 'NestedLayoutPanel',
43325         region: 'west',
43326         layout: {
43327            center: { },
43328            west: { }   
43329         },
43330         items : [ ... list of content panels or nested layout panels.. ]
43331    }
43332 );
43333 </code></pre>
43334      * @param {Object} cfg Xtype definition of item to add.
43335      */
43336     addxtype : function(cfg)
43337     {
43338         // basically accepts a pannel...
43339         // can accept a layout region..!?!?
43340         //Roo.log('Roo.BorderLayout add ' + cfg.xtype)
43341         
43342         
43343         // theory?  children can only be panels??
43344         
43345         //if (!cfg.xtype.match(/Panel$/)) {
43346         //    return false;
43347         //}
43348         var ret = false;
43349         
43350         if (typeof(cfg.region) == 'undefined') {
43351             Roo.log("Failed to add Panel, region was not set");
43352             Roo.log(cfg);
43353             return false;
43354         }
43355         var region = cfg.region;
43356         delete cfg.region;
43357         
43358           
43359         var xitems = [];
43360         if (cfg.items) {
43361             xitems = cfg.items;
43362             delete cfg.items;
43363         }
43364         var nb = false;
43365         
43366         if ( region == 'center') {
43367             Roo.log("Center: " + cfg.title);
43368         }
43369         
43370         
43371         switch(cfg.xtype) 
43372         {
43373             case 'Content':  // ContentPanel (el, cfg)
43374             case 'Scroll':  // ContentPanel (el, cfg)
43375             case 'View': 
43376                 cfg.autoCreate = cfg.autoCreate || true;
43377                 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
43378                 //} else {
43379                 //    var el = this.el.createChild();
43380                 //    ret = new Roo[cfg.xtype](el, cfg); // new panel!!!!!
43381                 //}
43382                 
43383                 this.add(region, ret);
43384                 break;
43385             
43386             /*
43387             case 'TreePanel': // our new panel!
43388                 cfg.el = this.el.createChild();
43389                 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
43390                 this.add(region, ret);
43391                 break;
43392             */
43393             
43394             case 'Nest': 
43395                 // create a new Layout (which is  a Border Layout...
43396                 
43397                 var clayout = cfg.layout;
43398                 clayout.el  = this.el.createChild();
43399                 clayout.items   = clayout.items  || [];
43400                 
43401                 delete cfg.layout;
43402                 
43403                 // replace this exitems with the clayout ones..
43404                 xitems = clayout.items;
43405                  
43406                 // force background off if it's in center...
43407                 if (region == 'center' && this.active && this.getRegion('center').panels.length < 1) {
43408                     cfg.background = false;
43409                 }
43410                 cfg.layout  = new Roo.bootstrap.layout.Border(clayout);
43411                 
43412                 
43413                 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
43414                 //console.log('adding nested layout panel '  + cfg.toSource());
43415                 this.add(region, ret);
43416                 nb = {}; /// find first...
43417                 break;
43418             
43419             case 'Grid':
43420                 
43421                 // needs grid and region
43422                 
43423                 //var el = this.getRegion(region).el.createChild();
43424                 /*
43425                  *var el = this.el.createChild();
43426                 // create the grid first...
43427                 cfg.grid.container = el;
43428                 cfg.grid = new cfg.grid.xns[cfg.grid.xtype](cfg.grid);
43429                 */
43430                 
43431                 if (region == 'center' && this.active ) {
43432                     cfg.background = false;
43433                 }
43434                 
43435                 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
43436                 
43437                 this.add(region, ret);
43438                 /*
43439                 if (cfg.background) {
43440                     // render grid on panel activation (if panel background)
43441                     ret.on('activate', function(gp) {
43442                         if (!gp.grid.rendered) {
43443                     //        gp.grid.render(el);
43444                         }
43445                     });
43446                 } else {
43447                   //  cfg.grid.render(el);
43448                 }
43449                 */
43450                 break;
43451            
43452            
43453             case 'Border': // it can get called on it'self... - might need to check if this is fixed?
43454                 // it was the old xcomponent building that caused this before.
43455                 // espeically if border is the top element in the tree.
43456                 ret = this;
43457                 break; 
43458                 
43459                     
43460                 
43461                 
43462                 
43463             default:
43464                 /*
43465                 if (typeof(Roo[cfg.xtype]) != 'undefined') {
43466                     
43467                     ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
43468                     this.add(region, ret);
43469                 } else {
43470                 */
43471                     Roo.log(cfg);
43472                     throw "Can not add '" + cfg.xtype + "' to Border";
43473                     return null;
43474              
43475                                 
43476              
43477         }
43478         this.beginUpdate();
43479         // add children..
43480         var region = '';
43481         var abn = {};
43482         Roo.each(xitems, function(i)  {
43483             region = nb && i.region ? i.region : false;
43484             
43485             var add = ret.addxtype(i);
43486            
43487             if (region) {
43488                 nb[region] = nb[region] == undefined ? 0 : nb[region]+1;
43489                 if (!i.background) {
43490                     abn[region] = nb[region] ;
43491                 }
43492             }
43493             
43494         });
43495         this.endUpdate();
43496
43497         // make the last non-background panel active..
43498         //if (nb) { Roo.log(abn); }
43499         if (nb) {
43500             
43501             for(var r in abn) {
43502                 region = this.getRegion(r);
43503                 if (region) {
43504                     // tried using nb[r], but it does not work..
43505                      
43506                     region.showPanel(abn[r]);
43507                    
43508                 }
43509             }
43510         }
43511         return ret;
43512         
43513     },
43514     
43515     
43516 // private
43517     factory : function(cfg)
43518     {
43519         
43520         var validRegions = Roo.bootstrap.layout.Border.regions;
43521
43522         var target = cfg.region;
43523         cfg.mgr = this;
43524         
43525         var r = Roo.bootstrap.layout;
43526         Roo.log(target);
43527         switch(target){
43528             case "north":
43529                 return new r.North(cfg);
43530             case "south":
43531                 return new r.South(cfg);
43532             case "east":
43533                 return new r.East(cfg);
43534             case "west":
43535                 return new r.West(cfg);
43536             case "center":
43537                 return new r.Center(cfg);
43538         }
43539         throw 'Layout region "'+target+'" not supported.';
43540     }
43541     
43542     
43543 });
43544  /*
43545  * Based on:
43546  * Ext JS Library 1.1.1
43547  * Copyright(c) 2006-2007, Ext JS, LLC.
43548  *
43549  * Originally Released Under LGPL - original licence link has changed is not relivant.
43550  *
43551  * Fork - LGPL
43552  * <script type="text/javascript">
43553  */
43554  
43555 /**
43556  * @class Roo.bootstrap.layout.Basic
43557  * @extends Roo.util.Observable
43558  * This class represents a lightweight region in a layout manager. This region does not move dom nodes
43559  * and does not have a titlebar, tabs or any other features. All it does is size and position 
43560  * panels. To create a BasicLayoutRegion, add lightweight:true or basic:true to your regions config.
43561  * @cfg {Roo.bootstrap.layout.Manager}   mgr The manager
43562  * @cfg {string}   region  the region that it inhabits..
43563  * @cfg {bool}   skipConfig skip config?
43564  * 
43565
43566  */
43567 Roo.bootstrap.layout.Basic = function(config){
43568     
43569     this.mgr = config.mgr;
43570     
43571     this.position = config.region;
43572     
43573     var skipConfig = config.skipConfig;
43574     
43575     this.events = {
43576         /**
43577          * @scope Roo.BasicLayoutRegion
43578          */
43579         
43580         /**
43581          * @event beforeremove
43582          * Fires before a panel is removed (or closed). To cancel the removal set "e.cancel = true" on the event argument.
43583          * @param {Roo.LayoutRegion} this
43584          * @param {Roo.ContentPanel} panel The panel
43585          * @param {Object} e The cancel event object
43586          */
43587         "beforeremove" : true,
43588         /**
43589          * @event invalidated
43590          * Fires when the layout for this region is changed.
43591          * @param {Roo.LayoutRegion} this
43592          */
43593         "invalidated" : true,
43594         /**
43595          * @event visibilitychange
43596          * Fires when this region is shown or hidden 
43597          * @param {Roo.LayoutRegion} this
43598          * @param {Boolean} visibility true or false
43599          */
43600         "visibilitychange" : true,
43601         /**
43602          * @event paneladded
43603          * Fires when a panel is added. 
43604          * @param {Roo.LayoutRegion} this
43605          * @param {Roo.ContentPanel} panel The panel
43606          */
43607         "paneladded" : true,
43608         /**
43609          * @event panelremoved
43610          * Fires when a panel is removed. 
43611          * @param {Roo.LayoutRegion} this
43612          * @param {Roo.ContentPanel} panel The panel
43613          */
43614         "panelremoved" : true,
43615         /**
43616          * @event beforecollapse
43617          * Fires when this region before collapse.
43618          * @param {Roo.LayoutRegion} this
43619          */
43620         "beforecollapse" : true,
43621         /**
43622          * @event collapsed
43623          * Fires when this region is collapsed.
43624          * @param {Roo.LayoutRegion} this
43625          */
43626         "collapsed" : true,
43627         /**
43628          * @event expanded
43629          * Fires when this region is expanded.
43630          * @param {Roo.LayoutRegion} this
43631          */
43632         "expanded" : true,
43633         /**
43634          * @event slideshow
43635          * Fires when this region is slid into view.
43636          * @param {Roo.LayoutRegion} this
43637          */
43638         "slideshow" : true,
43639         /**
43640          * @event slidehide
43641          * Fires when this region slides out of view. 
43642          * @param {Roo.LayoutRegion} this
43643          */
43644         "slidehide" : true,
43645         /**
43646          * @event panelactivated
43647          * Fires when a panel is activated. 
43648          * @param {Roo.LayoutRegion} this
43649          * @param {Roo.ContentPanel} panel The activated panel
43650          */
43651         "panelactivated" : true,
43652         /**
43653          * @event resized
43654          * Fires when the user resizes this region. 
43655          * @param {Roo.LayoutRegion} this
43656          * @param {Number} newSize The new size (width for east/west, height for north/south)
43657          */
43658         "resized" : true
43659     };
43660     /** A collection of panels in this region. @type Roo.util.MixedCollection */
43661     this.panels = new Roo.util.MixedCollection();
43662     this.panels.getKey = this.getPanelId.createDelegate(this);
43663     this.box = null;
43664     this.activePanel = null;
43665     // ensure listeners are added...
43666     
43667     if (config.listeners || config.events) {
43668         Roo.bootstrap.layout.Basic.superclass.constructor.call(this, {
43669             listeners : config.listeners || {},
43670             events : config.events || {}
43671         });
43672     }
43673     
43674     if(skipConfig !== true){
43675         this.applyConfig(config);
43676     }
43677 };
43678
43679 Roo.extend(Roo.bootstrap.layout.Basic, Roo.util.Observable,
43680 {
43681     getPanelId : function(p){
43682         return p.getId();
43683     },
43684     
43685     applyConfig : function(config){
43686         this.margins = config.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
43687         this.config = config;
43688         
43689     },
43690     
43691     /**
43692      * Resizes the region to the specified size. For vertical regions (west, east) this adjusts 
43693      * the width, for horizontal (north, south) the height.
43694      * @param {Number} newSize The new width or height
43695      */
43696     resizeTo : function(newSize){
43697         var el = this.el ? this.el :
43698                  (this.activePanel ? this.activePanel.getEl() : null);
43699         if(el){
43700             switch(this.position){
43701                 case "east":
43702                 case "west":
43703                     el.setWidth(newSize);
43704                     this.fireEvent("resized", this, newSize);
43705                 break;
43706                 case "north":
43707                 case "south":
43708                     el.setHeight(newSize);
43709                     this.fireEvent("resized", this, newSize);
43710                 break;                
43711             }
43712         }
43713     },
43714     
43715     getBox : function(){
43716         return this.activePanel ? this.activePanel.getEl().getBox(false, true) : null;
43717     },
43718     
43719     getMargins : function(){
43720         return this.margins;
43721     },
43722     
43723     updateBox : function(box){
43724         this.box = box;
43725         var el = this.activePanel.getEl();
43726         el.dom.style.left = box.x + "px";
43727         el.dom.style.top = box.y + "px";
43728         this.activePanel.setSize(box.width, box.height);
43729     },
43730     
43731     /**
43732      * Returns the container element for this region.
43733      * @return {Roo.Element}
43734      */
43735     getEl : function(){
43736         return this.activePanel;
43737     },
43738     
43739     /**
43740      * Returns true if this region is currently visible.
43741      * @return {Boolean}
43742      */
43743     isVisible : function(){
43744         return this.activePanel ? true : false;
43745     },
43746     
43747     setActivePanel : function(panel){
43748         panel = this.getPanel(panel);
43749         if(this.activePanel && this.activePanel != panel){
43750             this.activePanel.setActiveState(false);
43751             this.activePanel.getEl().setLeftTop(-10000,-10000);
43752         }
43753         this.activePanel = panel;
43754         panel.setActiveState(true);
43755         if(this.box){
43756             panel.setSize(this.box.width, this.box.height);
43757         }
43758         this.fireEvent("panelactivated", this, panel);
43759         this.fireEvent("invalidated");
43760     },
43761     
43762     /**
43763      * Show the specified panel.
43764      * @param {Number/String/ContentPanel} panelId The panels index, id or the panel itself
43765      * @return {Roo.ContentPanel} The shown panel or null
43766      */
43767     showPanel : function(panel){
43768         panel = this.getPanel(panel);
43769         if(panel){
43770             this.setActivePanel(panel);
43771         }
43772         return panel;
43773     },
43774     
43775     /**
43776      * Get the active panel for this region.
43777      * @return {Roo.ContentPanel} The active panel or null
43778      */
43779     getActivePanel : function(){
43780         return this.activePanel;
43781     },
43782     
43783     /**
43784      * Add the passed ContentPanel(s)
43785      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
43786      * @return {Roo.ContentPanel} The panel added (if only one was added)
43787      */
43788     add : function(panel){
43789         if(arguments.length > 1){
43790             for(var i = 0, len = arguments.length; i < len; i++) {
43791                 this.add(arguments[i]);
43792             }
43793             return null;
43794         }
43795         if(this.hasPanel(panel)){
43796             this.showPanel(panel);
43797             return panel;
43798         }
43799         var el = panel.getEl();
43800         if(el.dom.parentNode != this.mgr.el.dom){
43801             this.mgr.el.dom.appendChild(el.dom);
43802         }
43803         if(panel.setRegion){
43804             panel.setRegion(this);
43805         }
43806         this.panels.add(panel);
43807         el.setStyle("position", "absolute");
43808         if(!panel.background){
43809             this.setActivePanel(panel);
43810             if(this.config.initialSize && this.panels.getCount()==1){
43811                 this.resizeTo(this.config.initialSize);
43812             }
43813         }
43814         this.fireEvent("paneladded", this, panel);
43815         return panel;
43816     },
43817     
43818     /**
43819      * Returns true if the panel is in this region.
43820      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
43821      * @return {Boolean}
43822      */
43823     hasPanel : function(panel){
43824         if(typeof panel == "object"){ // must be panel obj
43825             panel = panel.getId();
43826         }
43827         return this.getPanel(panel) ? true : false;
43828     },
43829     
43830     /**
43831      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
43832      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
43833      * @param {Boolean} preservePanel Overrides the config preservePanel option
43834      * @return {Roo.ContentPanel} The panel that was removed
43835      */
43836     remove : function(panel, preservePanel){
43837         panel = this.getPanel(panel);
43838         if(!panel){
43839             return null;
43840         }
43841         var e = {};
43842         this.fireEvent("beforeremove", this, panel, e);
43843         if(e.cancel === true){
43844             return null;
43845         }
43846         var panelId = panel.getId();
43847         this.panels.removeKey(panelId);
43848         return panel;
43849     },
43850     
43851     /**
43852      * Returns the panel specified or null if it's not in this region.
43853      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
43854      * @return {Roo.ContentPanel}
43855      */
43856     getPanel : function(id){
43857         if(typeof id == "object"){ // must be panel obj
43858             return id;
43859         }
43860         return this.panels.get(id);
43861     },
43862     
43863     /**
43864      * Returns this regions position (north/south/east/west/center).
43865      * @return {String} 
43866      */
43867     getPosition: function(){
43868         return this.position;    
43869     }
43870 });/*
43871  * Based on:
43872  * Ext JS Library 1.1.1
43873  * Copyright(c) 2006-2007, Ext JS, LLC.
43874  *
43875  * Originally Released Under LGPL - original licence link has changed is not relivant.
43876  *
43877  * Fork - LGPL
43878  * <script type="text/javascript">
43879  */
43880  
43881 /**
43882  * @class Roo.bootstrap.layout.Region
43883  * @extends Roo.bootstrap.layout.Basic
43884  * This class represents a region in a layout manager.
43885  
43886  * @cfg {Object}    margins         Margins for the element (defaults to {top: 0, left: 0, right:0, bottom: 0})
43887  * @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})
43888  * @cfg {String}    tabPosition     (top|bottom) "top" or "bottom" (defaults to "bottom")
43889  * @cfg {Boolean}   alwaysShowTabs  True to always display tabs even when there is only 1 panel (defaults to false)
43890  * @cfg {Boolean}   autoScroll      True to enable overflow scrolling (defaults to false)
43891  * @cfg {Boolean}   titlebar        True to display a title bar (defaults to true)
43892  * @cfg {String}    title           The title for the region (overrides panel titles)
43893  * @cfg {Boolean}   animate         True to animate expand/collapse (defaults to false)
43894  * @cfg {Boolean}   autoHide        False to disable auto hiding when the mouse leaves the "floated" region (defaults to true)
43895  * @cfg {Boolean}   preservePanels  True to preserve removed panels so they can be readded later (defaults to false)
43896  * @cfg {Boolean}   closeOnTab      True to place the close icon on the tabs instead of the region titlebar (defaults to false)
43897  * @cfg {Boolean}   hideTabs        True to hide the tab strip (defaults to false)
43898  * @cfg {Boolean}   resizeTabs      True to enable automatic tab resizing. This will resize the tabs so they are all the same size and fit within
43899  *                      the space available, similar to FireFox 1.5 tabs (defaults to false)
43900  * @cfg {Number}    minTabWidth     The minimum tab width (defaults to 40)
43901  * @cfg {Number}    preferredTabWidth The preferred tab width (defaults to 150)
43902  * @cfg {String}    overflow       (hidden|visible) if you have menus in the region, then you need to set this to visible.
43903
43904  * @cfg {Boolean}   hidden          True to start the region hidden (defaults to false)
43905  * @cfg {Boolean}   hideWhenEmpty   True to hide the region when it has no panels
43906  * @cfg {Boolean}   disableTabTips  True to disable tab tooltips
43907  * @cfg {Number}    width           For East/West panels
43908  * @cfg {Number}    height          For North/South panels
43909  * @cfg {Boolean}   split           To show the splitter
43910  * @cfg {Boolean}   toolbar         xtype configuration for a toolbar - shows on right of tabbar
43911  * 
43912  * @cfg {string}   cls             Extra CSS classes to add to region
43913  * 
43914  * @cfg {Roo.bootstrap.layout.Manager}   mgr The manager
43915  * @cfg {string}   region  the region that it inhabits..
43916  *
43917
43918  * @xxxcfg {Boolean}   collapsible     DISABLED False to disable collapsing (defaults to true)
43919  * @xxxcfg {Boolean}   collapsed       DISABLED True to set the initial display to collapsed (defaults to false)
43920
43921  * @xxxcfg {String}    collapsedTitle  DISABLED Optional string message to display in the collapsed block of a north or south region
43922  * @xxxxcfg {Boolean}   floatable       DISABLED False to disable floating (defaults to true)
43923  * @xxxxcfg {Boolean}   showPin         True to show a pin button NOT SUPPORTED YET
43924  */
43925 Roo.bootstrap.layout.Region = function(config)
43926 {
43927     this.applyConfig(config);
43928
43929     var mgr = config.mgr;
43930     var pos = config.region;
43931     config.skipConfig = true;
43932     Roo.bootstrap.layout.Region.superclass.constructor.call(this, config);
43933     
43934     if (mgr.el) {
43935         this.onRender(mgr.el);   
43936     }
43937      
43938     this.visible = true;
43939     this.collapsed = false;
43940     this.unrendered_panels = [];
43941 };
43942
43943 Roo.extend(Roo.bootstrap.layout.Region, Roo.bootstrap.layout.Basic, {
43944
43945     position: '', // set by wrapper (eg. north/south etc..)
43946     unrendered_panels : null,  // unrendered panels.
43947     
43948     tabPosition : false,
43949     
43950     mgr: false, // points to 'Border'
43951     
43952     
43953     createBody : function(){
43954         /** This region's body element 
43955         * @type Roo.Element */
43956         this.bodyEl = this.el.createChild({
43957                 tag: "div",
43958                 cls: "roo-layout-panel-body tab-content" // bootstrap added...
43959         });
43960     },
43961
43962     onRender: function(ctr, pos)
43963     {
43964         var dh = Roo.DomHelper;
43965         /** This region's container element 
43966         * @type Roo.Element */
43967         this.el = dh.append(ctr.dom, {
43968                 tag: "div",
43969                 cls: (this.config.cls || '') + " roo-layout-region roo-layout-panel roo-layout-panel-" + this.position
43970             }, true);
43971         /** This region's title element 
43972         * @type Roo.Element */
43973     
43974         this.titleEl = dh.append(this.el.dom,  {
43975                 tag: "div",
43976                 unselectable: "on",
43977                 cls: "roo-unselectable roo-layout-panel-hd breadcrumb roo-layout-title-" + this.position,
43978                 children:[
43979                     {tag: "span", cls: "roo-unselectable roo-layout-panel-hd-text", unselectable: "on", html: "&#160;"},
43980                     {tag: "div", cls: "roo-unselectable roo-layout-panel-hd-tools", unselectable: "on"}
43981                 ]
43982             }, true);
43983         
43984         this.titleEl.enableDisplayMode();
43985         /** This region's title text element 
43986         * @type HTMLElement */
43987         this.titleTextEl = this.titleEl.dom.firstChild;
43988         this.tools = Roo.get(this.titleEl.dom.childNodes[1], true);
43989         /*
43990         this.closeBtn = this.createTool(this.tools.dom, "roo-layout-close");
43991         this.closeBtn.enableDisplayMode();
43992         this.closeBtn.on("click", this.closeClicked, this);
43993         this.closeBtn.hide();
43994     */
43995         this.createBody(this.config);
43996         if(this.config.hideWhenEmpty){
43997             this.hide();
43998             this.on("paneladded", this.validateVisibility, this);
43999             this.on("panelremoved", this.validateVisibility, this);
44000         }
44001         if(this.autoScroll){
44002             this.bodyEl.setStyle("overflow", "auto");
44003         }else{
44004             this.bodyEl.setStyle("overflow", this.config.overflow || 'hidden');
44005         }
44006         //if(c.titlebar !== false){
44007             if((!this.config.titlebar && !this.config.title) || this.config.titlebar === false){
44008                 this.titleEl.hide();
44009             }else{
44010                 this.titleEl.show();
44011                 if(this.config.title){
44012                     this.titleTextEl.innerHTML = this.config.title;
44013                 }
44014             }
44015         //}
44016         if(this.config.collapsed){
44017             this.collapse(true);
44018         }
44019         if(this.config.hidden){
44020             this.hide();
44021         }
44022         
44023         if (this.unrendered_panels && this.unrendered_panels.length) {
44024             for (var i =0;i< this.unrendered_panels.length; i++) {
44025                 this.add(this.unrendered_panels[i]);
44026             }
44027             this.unrendered_panels = null;
44028             
44029         }
44030         
44031     },
44032     
44033     applyConfig : function(c)
44034     {
44035         /*
44036          *if(c.collapsible && this.position != "center" && !this.collapsedEl){
44037             var dh = Roo.DomHelper;
44038             if(c.titlebar !== false){
44039                 this.collapseBtn = this.createTool(this.tools.dom, "roo-layout-collapse-"+this.position);
44040                 this.collapseBtn.on("click", this.collapse, this);
44041                 this.collapseBtn.enableDisplayMode();
44042                 /*
44043                 if(c.showPin === true || this.showPin){
44044                     this.stickBtn = this.createTool(this.tools.dom, "roo-layout-stick");
44045                     this.stickBtn.enableDisplayMode();
44046                     this.stickBtn.on("click", this.expand, this);
44047                     this.stickBtn.hide();
44048                 }
44049                 
44050             }
44051             */
44052             /** This region's collapsed element
44053             * @type Roo.Element */
44054             /*
44055              *
44056             this.collapsedEl = dh.append(this.mgr.el.dom, {cls: "x-layout-collapsed x-layout-collapsed-"+this.position, children:[
44057                 {cls: "x-layout-collapsed-tools", children:[{cls: "x-layout-ctools-inner"}]}
44058             ]}, true);
44059             
44060             if(c.floatable !== false){
44061                this.collapsedEl.addClassOnOver("x-layout-collapsed-over");
44062                this.collapsedEl.on("click", this.collapseClick, this);
44063             }
44064
44065             if(c.collapsedTitle && (this.position == "north" || this.position== "south")) {
44066                 this.collapsedTitleTextEl = dh.append(this.collapsedEl.dom, {tag: "div", cls: "x-unselectable x-layout-panel-hd-text",
44067                    id: "message", unselectable: "on", style:{"float":"left"}});
44068                this.collapsedTitleTextEl.innerHTML = c.collapsedTitle;
44069              }
44070             this.expandBtn = this.createTool(this.collapsedEl.dom.firstChild.firstChild, "x-layout-expand-"+this.position);
44071             this.expandBtn.on("click", this.expand, this);
44072             
44073         }
44074         
44075         if(this.collapseBtn){
44076             this.collapseBtn.setVisible(c.collapsible == true);
44077         }
44078         
44079         this.cmargins = c.cmargins || this.cmargins ||
44080                          (this.position == "west" || this.position == "east" ?
44081                              {top: 0, left: 2, right:2, bottom: 0} :
44082                              {top: 2, left: 0, right:0, bottom: 2});
44083         */
44084         this.margins = c.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
44085         
44086         
44087         this.tabPosition = [ 'top','bottom', 'west'].indexOf(c.tabPosition) > -1 ? c.tabPosition : "top";
44088         
44089         this.autoScroll = c.autoScroll || false;
44090         
44091         
44092        
44093         
44094         this.duration = c.duration || .30;
44095         this.slideDuration = c.slideDuration || .45;
44096         this.config = c;
44097        
44098     },
44099     /**
44100      * Returns true if this region is currently visible.
44101      * @return {Boolean}
44102      */
44103     isVisible : function(){
44104         return this.visible;
44105     },
44106
44107     /**
44108      * Updates the title for collapsed north/south regions (used with {@link #collapsedTitle} config option)
44109      * @param {String} title (optional) The title text (accepts HTML markup, defaults to the numeric character reference for a non-breaking space, "&amp;#160;")
44110      */
44111     //setCollapsedTitle : function(title){
44112     //    title = title || "&#160;";
44113      //   if(this.collapsedTitleTextEl){
44114       //      this.collapsedTitleTextEl.innerHTML = title;
44115        // }
44116     //},
44117
44118     getBox : function(){
44119         var b;
44120       //  if(!this.collapsed){
44121             b = this.el.getBox(false, true);
44122        // }else{
44123           //  b = this.collapsedEl.getBox(false, true);
44124         //}
44125         return b;
44126     },
44127
44128     getMargins : function(){
44129         return this.margins;
44130         //return this.collapsed ? this.cmargins : this.margins;
44131     },
44132 /*
44133     highlight : function(){
44134         this.el.addClass("x-layout-panel-dragover");
44135     },
44136
44137     unhighlight : function(){
44138         this.el.removeClass("x-layout-panel-dragover");
44139     },
44140 */
44141     updateBox : function(box)
44142     {
44143         if (!this.bodyEl) {
44144             return; // not rendered yet..
44145         }
44146         
44147         this.box = box;
44148         if(!this.collapsed){
44149             this.el.dom.style.left = box.x + "px";
44150             this.el.dom.style.top = box.y + "px";
44151             this.updateBody(box.width, box.height);
44152         }else{
44153             this.collapsedEl.dom.style.left = box.x + "px";
44154             this.collapsedEl.dom.style.top = box.y + "px";
44155             this.collapsedEl.setSize(box.width, box.height);
44156         }
44157         if(this.tabs){
44158             this.tabs.autoSizeTabs();
44159         }
44160     },
44161
44162     updateBody : function(w, h)
44163     {
44164         if(w !== null){
44165             this.el.setWidth(w);
44166             w -= this.el.getBorderWidth("rl");
44167             if(this.config.adjustments){
44168                 w += this.config.adjustments[0];
44169             }
44170         }
44171         if(h !== null && h > 0){
44172             this.el.setHeight(h);
44173             h = this.titleEl && this.titleEl.isDisplayed() ? h - (this.titleEl.getHeight()||0) : h;
44174             h -= this.el.getBorderWidth("tb");
44175             if(this.config.adjustments){
44176                 h += this.config.adjustments[1];
44177             }
44178             this.bodyEl.setHeight(h);
44179             if(this.tabs){
44180                 h = this.tabs.syncHeight(h);
44181             }
44182         }
44183         if(this.panelSize){
44184             w = w !== null ? w : this.panelSize.width;
44185             h = h !== null ? h : this.panelSize.height;
44186         }
44187         if(this.activePanel){
44188             var el = this.activePanel.getEl();
44189             w = w !== null ? w : el.getWidth();
44190             h = h !== null ? h : el.getHeight();
44191             this.panelSize = {width: w, height: h};
44192             this.activePanel.setSize(w, h);
44193         }
44194         if(Roo.isIE && this.tabs){
44195             this.tabs.el.repaint();
44196         }
44197     },
44198
44199     /**
44200      * Returns the container element for this region.
44201      * @return {Roo.Element}
44202      */
44203     getEl : function(){
44204         return this.el;
44205     },
44206
44207     /**
44208      * Hides this region.
44209      */
44210     hide : function(){
44211         //if(!this.collapsed){
44212             this.el.dom.style.left = "-2000px";
44213             this.el.hide();
44214         //}else{
44215          //   this.collapsedEl.dom.style.left = "-2000px";
44216          //   this.collapsedEl.hide();
44217        // }
44218         this.visible = false;
44219         this.fireEvent("visibilitychange", this, false);
44220     },
44221
44222     /**
44223      * Shows this region if it was previously hidden.
44224      */
44225     show : function(){
44226         //if(!this.collapsed){
44227             this.el.show();
44228         //}else{
44229         //    this.collapsedEl.show();
44230        // }
44231         this.visible = true;
44232         this.fireEvent("visibilitychange", this, true);
44233     },
44234 /*
44235     closeClicked : function(){
44236         if(this.activePanel){
44237             this.remove(this.activePanel);
44238         }
44239     },
44240
44241     collapseClick : function(e){
44242         if(this.isSlid){
44243            e.stopPropagation();
44244            this.slideIn();
44245         }else{
44246            e.stopPropagation();
44247            this.slideOut();
44248         }
44249     },
44250 */
44251     /**
44252      * Collapses this region.
44253      * @param {Boolean} skipAnim (optional) true to collapse the element without animation (if animate is true)
44254      */
44255     /*
44256     collapse : function(skipAnim, skipCheck = false){
44257         if(this.collapsed) {
44258             return;
44259         }
44260         
44261         if(skipCheck || this.fireEvent("beforecollapse", this) != false){
44262             
44263             this.collapsed = true;
44264             if(this.split){
44265                 this.split.el.hide();
44266             }
44267             if(this.config.animate && skipAnim !== true){
44268                 this.fireEvent("invalidated", this);
44269                 this.animateCollapse();
44270             }else{
44271                 this.el.setLocation(-20000,-20000);
44272                 this.el.hide();
44273                 this.collapsedEl.show();
44274                 this.fireEvent("collapsed", this);
44275                 this.fireEvent("invalidated", this);
44276             }
44277         }
44278         
44279     },
44280 */
44281     animateCollapse : function(){
44282         // overridden
44283     },
44284
44285     /**
44286      * Expands this region if it was previously collapsed.
44287      * @param {Roo.EventObject} e The event that triggered the expand (or null if calling manually)
44288      * @param {Boolean} skipAnim (optional) true to expand the element without animation (if animate is true)
44289      */
44290     /*
44291     expand : function(e, skipAnim){
44292         if(e) {
44293             e.stopPropagation();
44294         }
44295         if(!this.collapsed || this.el.hasActiveFx()) {
44296             return;
44297         }
44298         if(this.isSlid){
44299             this.afterSlideIn();
44300             skipAnim = true;
44301         }
44302         this.collapsed = false;
44303         if(this.config.animate && skipAnim !== true){
44304             this.animateExpand();
44305         }else{
44306             this.el.show();
44307             if(this.split){
44308                 this.split.el.show();
44309             }
44310             this.collapsedEl.setLocation(-2000,-2000);
44311             this.collapsedEl.hide();
44312             this.fireEvent("invalidated", this);
44313             this.fireEvent("expanded", this);
44314         }
44315     },
44316 */
44317     animateExpand : function(){
44318         // overridden
44319     },
44320
44321     initTabs : function()
44322     {
44323         //this.bodyEl.setStyle("overflow", "hidden"); -- this is set in render?
44324         
44325         var ts = new Roo.bootstrap.panel.Tabs({
44326             el: this.bodyEl.dom,
44327             region : this,
44328             tabPosition: this.tabPosition ? this.tabPosition  : 'top',
44329             disableTooltips: this.config.disableTabTips,
44330             toolbar : this.config.toolbar
44331         });
44332         
44333         if(this.config.hideTabs){
44334             ts.stripWrap.setDisplayed(false);
44335         }
44336         this.tabs = ts;
44337         ts.resizeTabs = this.config.resizeTabs === true;
44338         ts.minTabWidth = this.config.minTabWidth || 40;
44339         ts.maxTabWidth = this.config.maxTabWidth || 250;
44340         ts.preferredTabWidth = this.config.preferredTabWidth || 150;
44341         ts.monitorResize = false;
44342         //ts.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden"); // this is set in render?
44343         ts.bodyEl.addClass('roo-layout-tabs-body');
44344         this.panels.each(this.initPanelAsTab, this);
44345     },
44346
44347     initPanelAsTab : function(panel){
44348         var ti = this.tabs.addTab(
44349             panel.getEl().id,
44350             panel.getTitle(),
44351             null,
44352             this.config.closeOnTab && panel.isClosable(),
44353             panel.tpl
44354         );
44355         if(panel.tabTip !== undefined){
44356             ti.setTooltip(panel.tabTip);
44357         }
44358         ti.on("activate", function(){
44359               this.setActivePanel(panel);
44360         }, this);
44361         
44362         if(this.config.closeOnTab){
44363             ti.on("beforeclose", function(t, e){
44364                 e.cancel = true;
44365                 this.remove(panel);
44366             }, this);
44367         }
44368         
44369         panel.tabItem = ti;
44370         
44371         return ti;
44372     },
44373
44374     updatePanelTitle : function(panel, title)
44375     {
44376         if(this.activePanel == panel){
44377             this.updateTitle(title);
44378         }
44379         if(this.tabs){
44380             var ti = this.tabs.getTab(panel.getEl().id);
44381             ti.setText(title);
44382             if(panel.tabTip !== undefined){
44383                 ti.setTooltip(panel.tabTip);
44384             }
44385         }
44386     },
44387
44388     updateTitle : function(title){
44389         if(this.titleTextEl && !this.config.title){
44390             this.titleTextEl.innerHTML = (typeof title != "undefined" && title.length > 0 ? title : "&#160;");
44391         }
44392     },
44393
44394     setActivePanel : function(panel)
44395     {
44396         panel = this.getPanel(panel);
44397         if(this.activePanel && this.activePanel != panel){
44398             if(this.activePanel.setActiveState(false) === false){
44399                 return;
44400             }
44401         }
44402         this.activePanel = panel;
44403         panel.setActiveState(true);
44404         if(this.panelSize){
44405             panel.setSize(this.panelSize.width, this.panelSize.height);
44406         }
44407         if(this.closeBtn){
44408             this.closeBtn.setVisible(!this.config.closeOnTab && !this.isSlid && panel.isClosable());
44409         }
44410         this.updateTitle(panel.getTitle());
44411         if(this.tabs){
44412             this.fireEvent("invalidated", this);
44413         }
44414         this.fireEvent("panelactivated", this, panel);
44415     },
44416
44417     /**
44418      * Shows the specified panel.
44419      * @param {Number/String/ContentPanel} panelId The panel's index, id or the panel itself
44420      * @return {Roo.ContentPanel} The shown panel, or null if a panel could not be found from panelId
44421      */
44422     showPanel : function(panel)
44423     {
44424         panel = this.getPanel(panel);
44425         if(panel){
44426             if(this.tabs){
44427                 var tab = this.tabs.getTab(panel.getEl().id);
44428                 if(tab.isHidden()){
44429                     this.tabs.unhideTab(tab.id);
44430                 }
44431                 tab.activate();
44432             }else{
44433                 this.setActivePanel(panel);
44434             }
44435         }
44436         return panel;
44437     },
44438
44439     /**
44440      * Get the active panel for this region.
44441      * @return {Roo.ContentPanel} The active panel or null
44442      */
44443     getActivePanel : function(){
44444         return this.activePanel;
44445     },
44446
44447     validateVisibility : function(){
44448         if(this.panels.getCount() < 1){
44449             this.updateTitle("&#160;");
44450             this.closeBtn.hide();
44451             this.hide();
44452         }else{
44453             if(!this.isVisible()){
44454                 this.show();
44455             }
44456         }
44457     },
44458
44459     /**
44460      * Adds the passed ContentPanel(s) to this region.
44461      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
44462      * @return {Roo.ContentPanel} The panel added (if only one was added; null otherwise)
44463      */
44464     add : function(panel)
44465     {
44466         if(arguments.length > 1){
44467             for(var i = 0, len = arguments.length; i < len; i++) {
44468                 this.add(arguments[i]);
44469             }
44470             return null;
44471         }
44472         
44473         // if we have not been rendered yet, then we can not really do much of this..
44474         if (!this.bodyEl) {
44475             this.unrendered_panels.push(panel);
44476             return panel;
44477         }
44478         
44479         
44480         
44481         
44482         if(this.hasPanel(panel)){
44483             this.showPanel(panel);
44484             return panel;
44485         }
44486         panel.setRegion(this);
44487         this.panels.add(panel);
44488        /* if(this.panels.getCount() == 1 && !this.config.alwaysShowTabs){
44489             // sinle panel - no tab...?? would it not be better to render it with the tabs,
44490             // and hide them... ???
44491             this.bodyEl.dom.appendChild(panel.getEl().dom);
44492             if(panel.background !== true){
44493                 this.setActivePanel(panel);
44494             }
44495             this.fireEvent("paneladded", this, panel);
44496             return panel;
44497         }
44498         */
44499         if(!this.tabs){
44500             this.initTabs();
44501         }else{
44502             this.initPanelAsTab(panel);
44503         }
44504         
44505         
44506         if(panel.background !== true){
44507             this.tabs.activate(panel.getEl().id);
44508         }
44509         this.fireEvent("paneladded", this, panel);
44510         return panel;
44511     },
44512
44513     /**
44514      * Hides the tab for the specified panel.
44515      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
44516      */
44517     hidePanel : function(panel){
44518         if(this.tabs && (panel = this.getPanel(panel))){
44519             this.tabs.hideTab(panel.getEl().id);
44520         }
44521     },
44522
44523     /**
44524      * Unhides the tab for a previously hidden panel.
44525      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
44526      */
44527     unhidePanel : function(panel){
44528         if(this.tabs && (panel = this.getPanel(panel))){
44529             this.tabs.unhideTab(panel.getEl().id);
44530         }
44531     },
44532
44533     clearPanels : function(){
44534         while(this.panels.getCount() > 0){
44535              this.remove(this.panels.first());
44536         }
44537     },
44538
44539     /**
44540      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
44541      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
44542      * @param {Boolean} preservePanel Overrides the config preservePanel option
44543      * @return {Roo.ContentPanel} The panel that was removed
44544      */
44545     remove : function(panel, preservePanel)
44546     {
44547         panel = this.getPanel(panel);
44548         if(!panel){
44549             return null;
44550         }
44551         var e = {};
44552         this.fireEvent("beforeremove", this, panel, e);
44553         if(e.cancel === true){
44554             return null;
44555         }
44556         preservePanel = (typeof preservePanel != "undefined" ? preservePanel : (this.config.preservePanels === true || panel.preserve === true));
44557         var panelId = panel.getId();
44558         this.panels.removeKey(panelId);
44559         if(preservePanel){
44560             document.body.appendChild(panel.getEl().dom);
44561         }
44562         if(this.tabs){
44563             this.tabs.removeTab(panel.getEl().id);
44564         }else if (!preservePanel){
44565             this.bodyEl.dom.removeChild(panel.getEl().dom);
44566         }
44567         if(this.panels.getCount() == 1 && this.tabs && !this.config.alwaysShowTabs){
44568             var p = this.panels.first();
44569             var tempEl = document.createElement("div"); // temp holder to keep IE from deleting the node
44570             tempEl.appendChild(p.getEl().dom);
44571             this.bodyEl.update("");
44572             this.bodyEl.dom.appendChild(p.getEl().dom);
44573             tempEl = null;
44574             this.updateTitle(p.getTitle());
44575             this.tabs = null;
44576             this.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
44577             this.setActivePanel(p);
44578         }
44579         panel.setRegion(null);
44580         if(this.activePanel == panel){
44581             this.activePanel = null;
44582         }
44583         if(this.config.autoDestroy !== false && preservePanel !== true){
44584             try{panel.destroy();}catch(e){}
44585         }
44586         this.fireEvent("panelremoved", this, panel);
44587         return panel;
44588     },
44589
44590     /**
44591      * Returns the TabPanel component used by this region
44592      * @return {Roo.TabPanel}
44593      */
44594     getTabs : function(){
44595         return this.tabs;
44596     },
44597
44598     createTool : function(parentEl, className){
44599         var btn = Roo.DomHelper.append(parentEl, {
44600             tag: "div",
44601             cls: "x-layout-tools-button",
44602             children: [ {
44603                 tag: "div",
44604                 cls: "roo-layout-tools-button-inner " + className,
44605                 html: "&#160;"
44606             }]
44607         }, true);
44608         btn.addClassOnOver("roo-layout-tools-button-over");
44609         return btn;
44610     }
44611 });/*
44612  * Based on:
44613  * Ext JS Library 1.1.1
44614  * Copyright(c) 2006-2007, Ext JS, LLC.
44615  *
44616  * Originally Released Under LGPL - original licence link has changed is not relivant.
44617  *
44618  * Fork - LGPL
44619  * <script type="text/javascript">
44620  */
44621  
44622
44623
44624 /**
44625  * @class Roo.SplitLayoutRegion
44626  * @extends Roo.LayoutRegion
44627  * Adds a splitbar and other (private) useful functionality to a {@link Roo.LayoutRegion}.
44628  */
44629 Roo.bootstrap.layout.Split = function(config){
44630     this.cursor = config.cursor;
44631     Roo.bootstrap.layout.Split.superclass.constructor.call(this, config);
44632 };
44633
44634 Roo.extend(Roo.bootstrap.layout.Split, Roo.bootstrap.layout.Region,
44635 {
44636     splitTip : "Drag to resize.",
44637     collapsibleSplitTip : "Drag to resize. Double click to hide.",
44638     useSplitTips : false,
44639
44640     applyConfig : function(config){
44641         Roo.bootstrap.layout.Split.superclass.applyConfig.call(this, config);
44642     },
44643     
44644     onRender : function(ctr,pos) {
44645         
44646         Roo.bootstrap.layout.Split.superclass.onRender.call(this, ctr,pos);
44647         if(!this.config.split){
44648             return;
44649         }
44650         if(!this.split){
44651             
44652             var splitEl = Roo.DomHelper.append(ctr.dom,  {
44653                             tag: "div",
44654                             id: this.el.id + "-split",
44655                             cls: "roo-layout-split roo-layout-split-"+this.position,
44656                             html: "&#160;"
44657             });
44658             /** The SplitBar for this region 
44659             * @type Roo.SplitBar */
44660             // does not exist yet...
44661             Roo.log([this.position, this.orientation]);
44662             
44663             this.split = new Roo.bootstrap.SplitBar({
44664                 dragElement : splitEl,
44665                 resizingElement: this.el,
44666                 orientation : this.orientation
44667             });
44668             
44669             this.split.on("moved", this.onSplitMove, this);
44670             this.split.useShim = this.config.useShim === true;
44671             this.split.getMaximumSize = this[this.position == 'north' || this.position == 'south' ? 'getVMaxSize' : 'getHMaxSize'].createDelegate(this);
44672             if(this.useSplitTips){
44673                 this.split.el.dom.title = this.config.collapsible ? this.collapsibleSplitTip : this.splitTip;
44674             }
44675             //if(config.collapsible){
44676             //    this.split.el.on("dblclick", this.collapse,  this);
44677             //}
44678         }
44679         if(typeof this.config.minSize != "undefined"){
44680             this.split.minSize = this.config.minSize;
44681         }
44682         if(typeof this.config.maxSize != "undefined"){
44683             this.split.maxSize = this.config.maxSize;
44684         }
44685         if(this.config.hideWhenEmpty || this.config.hidden || this.config.collapsed){
44686             this.hideSplitter();
44687         }
44688         
44689     },
44690
44691     getHMaxSize : function(){
44692          var cmax = this.config.maxSize || 10000;
44693          var center = this.mgr.getRegion("center");
44694          return Math.min(cmax, (this.el.getWidth()+center.getEl().getWidth())-center.getMinWidth());
44695     },
44696
44697     getVMaxSize : function(){
44698          var cmax = this.config.maxSize || 10000;
44699          var center = this.mgr.getRegion("center");
44700          return Math.min(cmax, (this.el.getHeight()+center.getEl().getHeight())-center.getMinHeight());
44701     },
44702
44703     onSplitMove : function(split, newSize){
44704         this.fireEvent("resized", this, newSize);
44705     },
44706     
44707     /** 
44708      * Returns the {@link Roo.SplitBar} for this region.
44709      * @return {Roo.SplitBar}
44710      */
44711     getSplitBar : function(){
44712         return this.split;
44713     },
44714     
44715     hide : function(){
44716         this.hideSplitter();
44717         Roo.bootstrap.layout.Split.superclass.hide.call(this);
44718     },
44719
44720     hideSplitter : function(){
44721         if(this.split){
44722             this.split.el.setLocation(-2000,-2000);
44723             this.split.el.hide();
44724         }
44725     },
44726
44727     show : function(){
44728         if(this.split){
44729             this.split.el.show();
44730         }
44731         Roo.bootstrap.layout.Split.superclass.show.call(this);
44732     },
44733     
44734     beforeSlide: function(){
44735         if(Roo.isGecko){// firefox overflow auto bug workaround
44736             this.bodyEl.clip();
44737             if(this.tabs) {
44738                 this.tabs.bodyEl.clip();
44739             }
44740             if(this.activePanel){
44741                 this.activePanel.getEl().clip();
44742                 
44743                 if(this.activePanel.beforeSlide){
44744                     this.activePanel.beforeSlide();
44745                 }
44746             }
44747         }
44748     },
44749     
44750     afterSlide : function(){
44751         if(Roo.isGecko){// firefox overflow auto bug workaround
44752             this.bodyEl.unclip();
44753             if(this.tabs) {
44754                 this.tabs.bodyEl.unclip();
44755             }
44756             if(this.activePanel){
44757                 this.activePanel.getEl().unclip();
44758                 if(this.activePanel.afterSlide){
44759                     this.activePanel.afterSlide();
44760                 }
44761             }
44762         }
44763     },
44764
44765     initAutoHide : function(){
44766         if(this.autoHide !== false){
44767             if(!this.autoHideHd){
44768                 var st = new Roo.util.DelayedTask(this.slideIn, this);
44769                 this.autoHideHd = {
44770                     "mouseout": function(e){
44771                         if(!e.within(this.el, true)){
44772                             st.delay(500);
44773                         }
44774                     },
44775                     "mouseover" : function(e){
44776                         st.cancel();
44777                     },
44778                     scope : this
44779                 };
44780             }
44781             this.el.on(this.autoHideHd);
44782         }
44783     },
44784
44785     clearAutoHide : function(){
44786         if(this.autoHide !== false){
44787             this.el.un("mouseout", this.autoHideHd.mouseout);
44788             this.el.un("mouseover", this.autoHideHd.mouseover);
44789         }
44790     },
44791
44792     clearMonitor : function(){
44793         Roo.get(document).un("click", this.slideInIf, this);
44794     },
44795
44796     // these names are backwards but not changed for compat
44797     slideOut : function(){
44798         if(this.isSlid || this.el.hasActiveFx()){
44799             return;
44800         }
44801         this.isSlid = true;
44802         if(this.collapseBtn){
44803             this.collapseBtn.hide();
44804         }
44805         this.closeBtnState = this.closeBtn.getStyle('display');
44806         this.closeBtn.hide();
44807         if(this.stickBtn){
44808             this.stickBtn.show();
44809         }
44810         this.el.show();
44811         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor());
44812         this.beforeSlide();
44813         this.el.setStyle("z-index", 10001);
44814         this.el.slideIn(this.getSlideAnchor(), {
44815             callback: function(){
44816                 this.afterSlide();
44817                 this.initAutoHide();
44818                 Roo.get(document).on("click", this.slideInIf, this);
44819                 this.fireEvent("slideshow", this);
44820             },
44821             scope: this,
44822             block: true
44823         });
44824     },
44825
44826     afterSlideIn : function(){
44827         this.clearAutoHide();
44828         this.isSlid = false;
44829         this.clearMonitor();
44830         this.el.setStyle("z-index", "");
44831         if(this.collapseBtn){
44832             this.collapseBtn.show();
44833         }
44834         this.closeBtn.setStyle('display', this.closeBtnState);
44835         if(this.stickBtn){
44836             this.stickBtn.hide();
44837         }
44838         this.fireEvent("slidehide", this);
44839     },
44840
44841     slideIn : function(cb){
44842         if(!this.isSlid || this.el.hasActiveFx()){
44843             Roo.callback(cb);
44844             return;
44845         }
44846         this.isSlid = false;
44847         this.beforeSlide();
44848         this.el.slideOut(this.getSlideAnchor(), {
44849             callback: function(){
44850                 this.el.setLeftTop(-10000, -10000);
44851                 this.afterSlide();
44852                 this.afterSlideIn();
44853                 Roo.callback(cb);
44854             },
44855             scope: this,
44856             block: true
44857         });
44858     },
44859     
44860     slideInIf : function(e){
44861         if(!e.within(this.el)){
44862             this.slideIn();
44863         }
44864     },
44865
44866     animateCollapse : function(){
44867         this.beforeSlide();
44868         this.el.setStyle("z-index", 20000);
44869         var anchor = this.getSlideAnchor();
44870         this.el.slideOut(anchor, {
44871             callback : function(){
44872                 this.el.setStyle("z-index", "");
44873                 this.collapsedEl.slideIn(anchor, {duration:.3});
44874                 this.afterSlide();
44875                 this.el.setLocation(-10000,-10000);
44876                 this.el.hide();
44877                 this.fireEvent("collapsed", this);
44878             },
44879             scope: this,
44880             block: true
44881         });
44882     },
44883
44884     animateExpand : function(){
44885         this.beforeSlide();
44886         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor(), this.getExpandAdj());
44887         this.el.setStyle("z-index", 20000);
44888         this.collapsedEl.hide({
44889             duration:.1
44890         });
44891         this.el.slideIn(this.getSlideAnchor(), {
44892             callback : function(){
44893                 this.el.setStyle("z-index", "");
44894                 this.afterSlide();
44895                 if(this.split){
44896                     this.split.el.show();
44897                 }
44898                 this.fireEvent("invalidated", this);
44899                 this.fireEvent("expanded", this);
44900             },
44901             scope: this,
44902             block: true
44903         });
44904     },
44905
44906     anchors : {
44907         "west" : "left",
44908         "east" : "right",
44909         "north" : "top",
44910         "south" : "bottom"
44911     },
44912
44913     sanchors : {
44914         "west" : "l",
44915         "east" : "r",
44916         "north" : "t",
44917         "south" : "b"
44918     },
44919
44920     canchors : {
44921         "west" : "tl-tr",
44922         "east" : "tr-tl",
44923         "north" : "tl-bl",
44924         "south" : "bl-tl"
44925     },
44926
44927     getAnchor : function(){
44928         return this.anchors[this.position];
44929     },
44930
44931     getCollapseAnchor : function(){
44932         return this.canchors[this.position];
44933     },
44934
44935     getSlideAnchor : function(){
44936         return this.sanchors[this.position];
44937     },
44938
44939     getAlignAdj : function(){
44940         var cm = this.cmargins;
44941         switch(this.position){
44942             case "west":
44943                 return [0, 0];
44944             break;
44945             case "east":
44946                 return [0, 0];
44947             break;
44948             case "north":
44949                 return [0, 0];
44950             break;
44951             case "south":
44952                 return [0, 0];
44953             break;
44954         }
44955     },
44956
44957     getExpandAdj : function(){
44958         var c = this.collapsedEl, cm = this.cmargins;
44959         switch(this.position){
44960             case "west":
44961                 return [-(cm.right+c.getWidth()+cm.left), 0];
44962             break;
44963             case "east":
44964                 return [cm.right+c.getWidth()+cm.left, 0];
44965             break;
44966             case "north":
44967                 return [0, -(cm.top+cm.bottom+c.getHeight())];
44968             break;
44969             case "south":
44970                 return [0, cm.top+cm.bottom+c.getHeight()];
44971             break;
44972         }
44973     }
44974 });/*
44975  * Based on:
44976  * Ext JS Library 1.1.1
44977  * Copyright(c) 2006-2007, Ext JS, LLC.
44978  *
44979  * Originally Released Under LGPL - original licence link has changed is not relivant.
44980  *
44981  * Fork - LGPL
44982  * <script type="text/javascript">
44983  */
44984 /*
44985  * These classes are private internal classes
44986  */
44987 Roo.bootstrap.layout.Center = function(config){
44988     config.region = "center";
44989     Roo.bootstrap.layout.Region.call(this, config);
44990     this.visible = true;
44991     this.minWidth = config.minWidth || 20;
44992     this.minHeight = config.minHeight || 20;
44993 };
44994
44995 Roo.extend(Roo.bootstrap.layout.Center, Roo.bootstrap.layout.Region, {
44996     hide : function(){
44997         // center panel can't be hidden
44998     },
44999     
45000     show : function(){
45001         // center panel can't be hidden
45002     },
45003     
45004     getMinWidth: function(){
45005         return this.minWidth;
45006     },
45007     
45008     getMinHeight: function(){
45009         return this.minHeight;
45010     }
45011 });
45012
45013
45014
45015
45016  
45017
45018
45019
45020
45021
45022
45023 Roo.bootstrap.layout.North = function(config)
45024 {
45025     config.region = 'north';
45026     config.cursor = 'n-resize';
45027     
45028     Roo.bootstrap.layout.Split.call(this, config);
45029     
45030     
45031     if(this.split){
45032         this.split.placement = Roo.bootstrap.SplitBar.TOP;
45033         this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
45034         this.split.el.addClass("roo-layout-split-v");
45035     }
45036     //var size = config.initialSize || config.height;
45037     //if(this.el && typeof size != "undefined"){
45038     //    this.el.setHeight(size);
45039     //}
45040 };
45041 Roo.extend(Roo.bootstrap.layout.North, Roo.bootstrap.layout.Split,
45042 {
45043     orientation: Roo.bootstrap.SplitBar.VERTICAL,
45044      
45045      
45046     onRender : function(ctr, pos)
45047     {
45048         Roo.bootstrap.layout.Split.prototype.onRender.call(this, ctr, pos);
45049         var size = this.config.initialSize || this.config.height;
45050         if(this.el && typeof size != "undefined"){
45051             this.el.setHeight(size);
45052         }
45053     
45054     },
45055     
45056     getBox : function(){
45057         if(this.collapsed){
45058             return this.collapsedEl.getBox();
45059         }
45060         var box = this.el.getBox();
45061         if(this.split){
45062             box.height += this.split.el.getHeight();
45063         }
45064         return box;
45065     },
45066     
45067     updateBox : function(box){
45068         if(this.split && !this.collapsed){
45069             box.height -= this.split.el.getHeight();
45070             this.split.el.setLeft(box.x);
45071             this.split.el.setTop(box.y+box.height);
45072             this.split.el.setWidth(box.width);
45073         }
45074         if(this.collapsed){
45075             this.updateBody(box.width, null);
45076         }
45077         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
45078     }
45079 });
45080
45081
45082
45083
45084
45085 Roo.bootstrap.layout.South = function(config){
45086     config.region = 'south';
45087     config.cursor = 's-resize';
45088     Roo.bootstrap.layout.Split.call(this, config);
45089     if(this.split){
45090         this.split.placement = Roo.bootstrap.SplitBar.BOTTOM;
45091         this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
45092         this.split.el.addClass("roo-layout-split-v");
45093     }
45094     
45095 };
45096
45097 Roo.extend(Roo.bootstrap.layout.South, Roo.bootstrap.layout.Split, {
45098     orientation: Roo.bootstrap.SplitBar.VERTICAL,
45099     
45100     onRender : function(ctr, pos)
45101     {
45102         Roo.bootstrap.layout.Split.prototype.onRender.call(this, ctr, pos);
45103         var size = this.config.initialSize || this.config.height;
45104         if(this.el && typeof size != "undefined"){
45105             this.el.setHeight(size);
45106         }
45107     
45108     },
45109     
45110     getBox : function(){
45111         if(this.collapsed){
45112             return this.collapsedEl.getBox();
45113         }
45114         var box = this.el.getBox();
45115         if(this.split){
45116             var sh = this.split.el.getHeight();
45117             box.height += sh;
45118             box.y -= sh;
45119         }
45120         return box;
45121     },
45122     
45123     updateBox : function(box){
45124         if(this.split && !this.collapsed){
45125             var sh = this.split.el.getHeight();
45126             box.height -= sh;
45127             box.y += sh;
45128             this.split.el.setLeft(box.x);
45129             this.split.el.setTop(box.y-sh);
45130             this.split.el.setWidth(box.width);
45131         }
45132         if(this.collapsed){
45133             this.updateBody(box.width, null);
45134         }
45135         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
45136     }
45137 });
45138
45139 Roo.bootstrap.layout.East = function(config){
45140     config.region = "east";
45141     config.cursor = "e-resize";
45142     Roo.bootstrap.layout.Split.call(this, config);
45143     if(this.split){
45144         this.split.placement = Roo.bootstrap.SplitBar.RIGHT;
45145         this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
45146         this.split.el.addClass("roo-layout-split-h");
45147     }
45148     
45149 };
45150 Roo.extend(Roo.bootstrap.layout.East, Roo.bootstrap.layout.Split, {
45151     orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
45152     
45153     onRender : function(ctr, pos)
45154     {
45155         Roo.bootstrap.layout.Split.prototype.onRender.call(this, ctr, pos);
45156         var size = this.config.initialSize || this.config.width;
45157         if(this.el && typeof size != "undefined"){
45158             this.el.setWidth(size);
45159         }
45160     
45161     },
45162     
45163     getBox : function(){
45164         if(this.collapsed){
45165             return this.collapsedEl.getBox();
45166         }
45167         var box = this.el.getBox();
45168         if(this.split){
45169             var sw = this.split.el.getWidth();
45170             box.width += sw;
45171             box.x -= sw;
45172         }
45173         return box;
45174     },
45175
45176     updateBox : function(box){
45177         if(this.split && !this.collapsed){
45178             var sw = this.split.el.getWidth();
45179             box.width -= sw;
45180             this.split.el.setLeft(box.x);
45181             this.split.el.setTop(box.y);
45182             this.split.el.setHeight(box.height);
45183             box.x += sw;
45184         }
45185         if(this.collapsed){
45186             this.updateBody(null, box.height);
45187         }
45188         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
45189     }
45190 });
45191
45192 Roo.bootstrap.layout.West = function(config){
45193     config.region = "west";
45194     config.cursor = "w-resize";
45195     
45196     Roo.bootstrap.layout.Split.call(this, config);
45197     if(this.split){
45198         this.split.placement = Roo.bootstrap.SplitBar.LEFT;
45199         this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
45200         this.split.el.addClass("roo-layout-split-h");
45201     }
45202     
45203 };
45204 Roo.extend(Roo.bootstrap.layout.West, Roo.bootstrap.layout.Split, {
45205     orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
45206     
45207     onRender: function(ctr, pos)
45208     {
45209         Roo.bootstrap.layout.West.superclass.onRender.call(this, ctr,pos);
45210         var size = this.config.initialSize || this.config.width;
45211         if(typeof size != "undefined"){
45212             this.el.setWidth(size);
45213         }
45214     },
45215     
45216     getBox : function(){
45217         if(this.collapsed){
45218             return this.collapsedEl.getBox();
45219         }
45220         var box = this.el.getBox();
45221         if (box.width == 0) {
45222             box.width = this.config.width; // kludge?
45223         }
45224         if(this.split){
45225             box.width += this.split.el.getWidth();
45226         }
45227         return box;
45228     },
45229     
45230     updateBox : function(box){
45231         if(this.split && !this.collapsed){
45232             var sw = this.split.el.getWidth();
45233             box.width -= sw;
45234             this.split.el.setLeft(box.x+box.width);
45235             this.split.el.setTop(box.y);
45236             this.split.el.setHeight(box.height);
45237         }
45238         if(this.collapsed){
45239             this.updateBody(null, box.height);
45240         }
45241         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
45242     }
45243 });/*
45244  * Based on:
45245  * Ext JS Library 1.1.1
45246  * Copyright(c) 2006-2007, Ext JS, LLC.
45247  *
45248  * Originally Released Under LGPL - original licence link has changed is not relivant.
45249  *
45250  * Fork - LGPL
45251  * <script type="text/javascript">
45252  */
45253 /**
45254  * @class Roo.bootstrap.paenl.Content
45255  * @extends Roo.util.Observable
45256  * @children Roo.bootstrap.Component
45257  * @parent builder Roo.bootstrap.layout.Border
45258  * A basic ContentPanel element. - a panel that contain any content (eg. forms etc.)
45259  * @cfg {Boolean}   fitToFrame    True for this panel to adjust its size to fit when the region resizes  (defaults to false)
45260  * @cfg {Boolean}   fitContainer   When using {@link #fitToFrame} and {@link #resizeEl}, you can also fit the parent container  (defaults to false)
45261  * @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
45262  * @cfg {Boolean}   closable      True if the panel can be closed/removed
45263  * @cfg {Boolean}   background    True if the panel should not be activated when it is added (defaults to false)
45264  * @cfg {String/HTMLElement/Element} resizeEl An element to resize if {@link #fitToFrame} is true (instead of this panel's element)
45265  * @cfg {Toolbar}   toolbar       A toolbar for this panel
45266  * @cfg {Boolean} autoScroll    True to scroll overflow in this panel (use with {@link #fitToFrame})
45267  * @cfg {String} title          The title for this panel
45268  * @cfg {Array} adjustments     Values to <b>add</b> to the width/height when doing a {@link #fitToFrame} (default is [0, 0])
45269  * @cfg {String} url            Calls {@link #setUrl} with this value
45270  * @cfg {String} region  [required] (center|north|south|east|west) which region to put this panel on (when used with xtype constructors)
45271  * @cfg {String/Object} params  When used with {@link #url}, calls {@link #setUrl} with this value
45272  * @cfg {Boolean} loadOnce      When used with {@link #url}, calls {@link #setUrl} with this value
45273  * @cfg {String}    content        Raw content to fill content panel with (uses setContent on construction.)
45274  * @cfg {Boolean} iframe      contents are an iframe - makes showing remote sources/CSS feasible..
45275  * @cfg {Boolean} badges render the badges
45276  * @cfg {String} cls  extra classes to use  
45277  * @cfg {String} background (primary|secondary|success|info|warning|danger|light|dark)
45278  
45279  * @constructor
45280  * Create a new ContentPanel.
45281  * @param {String/Object} config A string to set only the title or a config object
45282  
45283  */
45284 Roo.bootstrap.panel.Content = function( config){
45285     
45286     this.tpl = config.tpl || false;
45287     
45288     var el = config.el;
45289     var content = config.content;
45290
45291     if(config.autoCreate){ // xtype is available if this is called from factory
45292         el = Roo.id();
45293     }
45294     this.el = Roo.get(el);
45295     if(!this.el && config && config.autoCreate){
45296         if(typeof config.autoCreate == "object"){
45297             if(!config.autoCreate.id){
45298                 config.autoCreate.id = config.id||el;
45299             }
45300             this.el = Roo.DomHelper.append(document.body,
45301                         config.autoCreate, true);
45302         }else{
45303             var elcfg =  {
45304                 tag: "div",
45305                 cls: (config.cls || '') +
45306                     (config.background ? ' bg-' + config.background : '') +
45307                     " roo-layout-inactive-content",
45308                 id: config.id||el
45309             };
45310             if (config.iframe) {
45311                 elcfg.cn = [
45312                     {
45313                         tag : 'iframe',
45314                         style : 'border: 0px',
45315                         src : 'about:blank'
45316                     }
45317                 ];
45318             }
45319               
45320             if (config.html) {
45321                 elcfg.html = config.html;
45322                 
45323             }
45324                         
45325             this.el = Roo.DomHelper.append(document.body, elcfg , true);
45326             if (config.iframe) {
45327                 this.iframeEl = this.el.select('iframe',true).first();
45328             }
45329             
45330         }
45331     } 
45332     this.closable = false;
45333     this.loaded = false;
45334     this.active = false;
45335    
45336       
45337     if (config.toolbar && !config.toolbar.el && config.toolbar.xtype) {
45338         
45339         this.toolbar = new config.toolbar.xns[config.toolbar.xtype](config.toolbar);
45340         
45341         this.wrapEl = this.el; //this.el.wrap();
45342         var ti = [];
45343         if (config.toolbar.items) {
45344             ti = config.toolbar.items ;
45345             delete config.toolbar.items ;
45346         }
45347         
45348         var nitems = [];
45349         this.toolbar.render(this.wrapEl, 'before');
45350         for(var i =0;i < ti.length;i++) {
45351           //  Roo.log(['add child', items[i]]);
45352             nitems.push(this.toolbar.addxtype(Roo.apply({}, ti[i])));
45353         }
45354         this.toolbar.items = nitems;
45355         this.toolbar.el.insertBefore(this.wrapEl.dom.firstChild);
45356         delete config.toolbar;
45357         
45358     }
45359     /*
45360     // xtype created footer. - not sure if will work as we normally have to render first..
45361     if (this.footer && !this.footer.el && this.footer.xtype) {
45362         if (!this.wrapEl) {
45363             this.wrapEl = this.el.wrap();
45364         }
45365     
45366         this.footer.container = this.wrapEl.createChild();
45367          
45368         this.footer = Roo.factory(this.footer, Roo);
45369         
45370     }
45371     */
45372     
45373      if(typeof config == "string"){
45374         this.title = config;
45375     }else{
45376         Roo.apply(this, config);
45377     }
45378     
45379     if(this.resizeEl){
45380         this.resizeEl = Roo.get(this.resizeEl, true);
45381     }else{
45382         this.resizeEl = this.el;
45383     }
45384     // handle view.xtype
45385     
45386  
45387     
45388     
45389     this.addEvents({
45390         /**
45391          * @event activate
45392          * Fires when this panel is activated. 
45393          * @param {Roo.ContentPanel} this
45394          */
45395         "activate" : true,
45396         /**
45397          * @event deactivate
45398          * Fires when this panel is activated. 
45399          * @param {Roo.ContentPanel} this
45400          */
45401         "deactivate" : true,
45402
45403         /**
45404          * @event resize
45405          * Fires when this panel is resized if fitToFrame is true.
45406          * @param {Roo.ContentPanel} this
45407          * @param {Number} width The width after any component adjustments
45408          * @param {Number} height The height after any component adjustments
45409          */
45410         "resize" : true,
45411         
45412          /**
45413          * @event render
45414          * Fires when this tab is created
45415          * @param {Roo.ContentPanel} this
45416          */
45417         "render" : true,
45418         
45419           /**
45420          * @event scroll
45421          * Fires when this content is scrolled
45422          * @param {Roo.ContentPanel} this
45423          * @param {Event} scrollEvent
45424          */
45425         "scroll" : true
45426         
45427         
45428         
45429     });
45430     
45431
45432     
45433     
45434     if(this.autoScroll && !this.iframe){
45435         this.resizeEl.setStyle("overflow", "auto");
45436         this.resizeEl.on('scroll', this.onScroll, this);
45437     } else {
45438         // fix randome scrolling
45439         //this.el.on('scroll', function() {
45440         //    Roo.log('fix random scolling');
45441         //    this.scrollTo('top',0); 
45442         //});
45443     }
45444     content = content || this.content;
45445     if(content){
45446         this.setContent(content);
45447     }
45448     if(config && config.url){
45449         this.setUrl(this.url, this.params, this.loadOnce);
45450     }
45451     
45452     
45453     
45454     Roo.bootstrap.panel.Content.superclass.constructor.call(this);
45455     
45456     if (this.view && typeof(this.view.xtype) != 'undefined') {
45457         this.view.el = this.el.appendChild(document.createElement("div"));
45458         this.view = Roo.factory(this.view); 
45459         this.view.render  &&  this.view.render(false, '');  
45460     }
45461     
45462     
45463     this.fireEvent('render', this);
45464 };
45465
45466 Roo.extend(Roo.bootstrap.panel.Content, Roo.bootstrap.Component, {
45467     
45468     cls : '',
45469     background : '',
45470     
45471     tabTip : '',
45472     
45473     iframe : false,
45474     iframeEl : false,
45475     
45476     /* Resize Element - use this to work out scroll etc. */
45477     resizeEl : false,
45478     
45479     setRegion : function(region){
45480         this.region = region;
45481         this.setActiveClass(region && !this.background);
45482     },
45483     
45484     
45485     setActiveClass: function(state)
45486     {
45487         if(state){
45488            this.el.replaceClass("roo-layout-inactive-content", "roo-layout-active-content");
45489            this.el.setStyle('position','relative');
45490         }else{
45491            this.el.replaceClass("roo-layout-active-content", "roo-layout-inactive-content");
45492            this.el.setStyle('position', 'absolute');
45493         } 
45494     },
45495     
45496     /**
45497      * Returns the toolbar for this Panel if one was configured. 
45498      * @return {Roo.Toolbar} 
45499      */
45500     getToolbar : function(){
45501         return this.toolbar;
45502     },
45503     
45504     setActiveState : function(active)
45505     {
45506         this.active = active;
45507         this.setActiveClass(active);
45508         if(!active){
45509             if(this.fireEvent("deactivate", this) === false){
45510                 return false;
45511             }
45512             return true;
45513         }
45514         this.fireEvent("activate", this);
45515         return true;
45516     },
45517     /**
45518      * Updates this panel's element (not for iframe)
45519      * @param {String} content The new content
45520      * @param {Boolean} loadScripts (optional) true to look for and process scripts
45521     */
45522     setContent : function(content, loadScripts){
45523         if (this.iframe) {
45524             return;
45525         }
45526         
45527         this.el.update(content, loadScripts);
45528     },
45529
45530     ignoreResize : function(w, h)
45531     {
45532         //return false; // always resize?
45533         if(this.lastSize && this.lastSize.width == w && this.lastSize.height == h){
45534             return true;
45535         }else{
45536             this.lastSize = {width: w, height: h};
45537             return false;
45538         }
45539     },
45540     /**
45541      * Get the {@link Roo.UpdateManager} for this panel. Enables you to perform Ajax updates.
45542      * @return {Roo.UpdateManager} The UpdateManager
45543      */
45544     getUpdateManager : function(){
45545         if (this.iframe) {
45546             return false;
45547         }
45548         return this.el.getUpdateManager();
45549     },
45550      /**
45551      * Loads this content panel immediately with content from XHR. Note: to delay loading until the panel is activated, use {@link #setUrl}.
45552      * Does not work with IFRAME contents
45553      * @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:
45554 <pre><code>
45555 panel.load({
45556     url: "your-url.php",
45557     params: {param1: "foo", param2: "bar"}, // or a URL encoded string
45558     callback: yourFunction,
45559     scope: yourObject, //(optional scope)
45560     discardUrl: false,
45561     nocache: false,
45562     text: "Loading...",
45563     timeout: 30,
45564     scripts: false
45565 });
45566 </code></pre>
45567      
45568      * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
45569      * 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.
45570      * @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}
45571      * @param {Function} callback (optional) Callback when transaction is complete -- called with signature (oElement, bSuccess, oResponse)
45572      * @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.
45573      * @return {Roo.ContentPanel} this
45574      */
45575     load : function(){
45576         
45577         if (this.iframe) {
45578             return this;
45579         }
45580         
45581         var um = this.el.getUpdateManager();
45582         um.update.apply(um, arguments);
45583         return this;
45584     },
45585
45586
45587     /**
45588      * 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.
45589      * @param {String/Function} url The URL to load the content from or a function to call to get the URL
45590      * @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)
45591      * @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)
45592      * @return {Roo.UpdateManager|Boolean} The UpdateManager or false if IFRAME
45593      */
45594     setUrl : function(url, params, loadOnce){
45595         if (this.iframe) {
45596             this.iframeEl.dom.src = url;
45597             return false;
45598         }
45599         
45600         if(this.refreshDelegate){
45601             this.removeListener("activate", this.refreshDelegate);
45602         }
45603         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
45604         this.on("activate", this.refreshDelegate);
45605         return this.el.getUpdateManager();
45606     },
45607     
45608     _handleRefresh : function(url, params, loadOnce){
45609         if(!loadOnce || !this.loaded){
45610             var updater = this.el.getUpdateManager();
45611             updater.update(url, params, this._setLoaded.createDelegate(this));
45612         }
45613     },
45614     
45615     _setLoaded : function(){
45616         this.loaded = true;
45617     }, 
45618     
45619     /**
45620      * Returns this panel's id
45621      * @return {String} 
45622      */
45623     getId : function(){
45624         return this.el.id;
45625     },
45626     
45627     /** 
45628      * Returns this panel's element - used by regiosn to add.
45629      * @return {Roo.Element} 
45630      */
45631     getEl : function(){
45632         return this.wrapEl || this.el;
45633     },
45634     
45635    
45636     
45637     adjustForComponents : function(width, height)
45638     {
45639         //Roo.log('adjustForComponents ');
45640         if(this.resizeEl != this.el){
45641             width -= this.el.getFrameWidth('lr');
45642             height -= this.el.getFrameWidth('tb');
45643         }
45644         if(this.toolbar){
45645             var te = this.toolbar.getEl();
45646             te.setWidth(width);
45647             height -= te.getHeight();
45648         }
45649         if(this.footer){
45650             var te = this.footer.getEl();
45651             te.setWidth(width);
45652             height -= te.getHeight();
45653         }
45654         
45655         
45656         if(this.adjustments){
45657             width += this.adjustments[0];
45658             height += this.adjustments[1];
45659         }
45660         return {"width": width, "height": height};
45661     },
45662     
45663     setSize : function(width, height){
45664         if(this.fitToFrame && !this.ignoreResize(width, height)){
45665             if(this.fitContainer && this.resizeEl != this.el){
45666                 this.el.setSize(width, height);
45667             }
45668             var size = this.adjustForComponents(width, height);
45669             if (this.iframe) {
45670                 this.iframeEl.setSize(width,height);
45671             }
45672             
45673             this.resizeEl.setSize(this.autoWidth ? "auto" : size.width, this.autoHeight ? "auto" : size.height);
45674             this.fireEvent('resize', this, size.width, size.height);
45675             
45676             
45677         }
45678     },
45679     
45680     /**
45681      * Returns this panel's title
45682      * @return {String} 
45683      */
45684     getTitle : function(){
45685         
45686         if (typeof(this.title) != 'object') {
45687             return this.title;
45688         }
45689         
45690         var t = '';
45691         for (var k in this.title) {
45692             if (!this.title.hasOwnProperty(k)) {
45693                 continue;
45694             }
45695             
45696             if (k.indexOf('-') >= 0) {
45697                 var s = k.split('-');
45698                 for (var i = 0; i<s.length; i++) {
45699                     t += "<span class='visible-"+s[i]+"'>"+this.title[k]+"</span>";
45700                 }
45701             } else {
45702                 t += "<span class='visible-"+k+"'>"+this.title[k]+"</span>";
45703             }
45704         }
45705         return t;
45706     },
45707     
45708     /**
45709      * Set this panel's title
45710      * @param {String} title
45711      */
45712     setTitle : function(title){
45713         this.title = title;
45714         if(this.region){
45715             this.region.updatePanelTitle(this, title);
45716         }
45717     },
45718     
45719     /**
45720      * Returns true is this panel was configured to be closable
45721      * @return {Boolean} 
45722      */
45723     isClosable : function(){
45724         return this.closable;
45725     },
45726     
45727     beforeSlide : function(){
45728         this.el.clip();
45729         this.resizeEl.clip();
45730     },
45731     
45732     afterSlide : function(){
45733         this.el.unclip();
45734         this.resizeEl.unclip();
45735     },
45736     
45737     /**
45738      *   Force a content refresh from the URL specified in the {@link #setUrl} method.
45739      *   Will fail silently if the {@link #setUrl} method has not been called.
45740      *   This does not activate the panel, just updates its content.
45741      */
45742     refresh : function(){
45743         if(this.refreshDelegate){
45744            this.loaded = false;
45745            this.refreshDelegate();
45746         }
45747     },
45748     
45749     /**
45750      * Destroys this panel
45751      */
45752     destroy : function(){
45753         this.el.removeAllListeners();
45754         var tempEl = document.createElement("span");
45755         tempEl.appendChild(this.el.dom);
45756         tempEl.innerHTML = "";
45757         this.el.remove();
45758         this.el = null;
45759     },
45760     
45761     /**
45762      * form - if the content panel contains a form - this is a reference to it.
45763      * @type {Roo.form.Form}
45764      */
45765     form : false,
45766     /**
45767      * view - if the content panel contains a view (Roo.DatePicker / Roo.View / Roo.JsonView)
45768      *    This contains a reference to it.
45769      * @type {Roo.View}
45770      */
45771     view : false,
45772     
45773       /**
45774      * Adds a xtype elements to the panel - currently only supports Forms, View, JsonView.
45775      * <pre><code>
45776
45777 layout.addxtype({
45778        xtype : 'Form',
45779        items: [ .... ]
45780    }
45781 );
45782
45783 </code></pre>
45784      * @param {Object} cfg Xtype definition of item to add.
45785      */
45786     
45787     
45788     getChildContainer: function () {
45789         return this.getEl();
45790     },
45791     
45792     
45793     onScroll : function(e)
45794     {
45795         this.fireEvent('scroll', this, e);
45796     }
45797     
45798     
45799     /*
45800         var  ret = new Roo.factory(cfg);
45801         return ret;
45802         
45803         
45804         // add form..
45805         if (cfg.xtype.match(/^Form$/)) {
45806             
45807             var el;
45808             //if (this.footer) {
45809             //    el = this.footer.container.insertSibling(false, 'before');
45810             //} else {
45811                 el = this.el.createChild();
45812             //}
45813
45814             this.form = new  Roo.form.Form(cfg);
45815             
45816             
45817             if ( this.form.allItems.length) {
45818                 this.form.render(el.dom);
45819             }
45820             return this.form;
45821         }
45822         // should only have one of theses..
45823         if ([ 'View', 'JsonView', 'DatePicker'].indexOf(cfg.xtype) > -1) {
45824             // views.. should not be just added - used named prop 'view''
45825             
45826             cfg.el = this.el.appendChild(document.createElement("div"));
45827             // factory?
45828             
45829             var ret = new Roo.factory(cfg);
45830              
45831              ret.render && ret.render(false, ''); // render blank..
45832             this.view = ret;
45833             return ret;
45834         }
45835         return false;
45836     }
45837     \*/
45838 });
45839  
45840 /**
45841  * @class Roo.bootstrap.panel.Grid
45842  * @extends Roo.bootstrap.panel.Content
45843  * @constructor
45844  * Create a new GridPanel.
45845  * @cfg {Roo.bootstrap.Table} grid The grid for this panel
45846  * @cfg {Roo.bootstrap.nav.Simplebar} toolbar the toolbar at the top of the grid.
45847  * @param {Object} config A the config object
45848   
45849  */
45850
45851
45852
45853 Roo.bootstrap.panel.Grid = function(config)
45854 {
45855     
45856       
45857     this.wrapper = Roo.DomHelper.append(document.body, // wrapper for IE7 strict & safari scroll issue
45858         {tag: "div", cls: "roo-layout-grid-wrapper roo-layout-inactive-content"}, true);
45859
45860     config.el = this.wrapper;
45861     //this.el = this.wrapper;
45862     
45863       if (config.container) {
45864         // ctor'ed from a Border/panel.grid
45865         
45866         
45867         this.wrapper.setStyle("overflow", "hidden");
45868         this.wrapper.addClass('roo-grid-container');
45869
45870     }
45871     
45872     
45873     if(config.toolbar){
45874         var tool_el = this.wrapper.createChild();    
45875         this.toolbar = Roo.factory(config.toolbar);
45876         var ti = [];
45877         if (config.toolbar.items) {
45878             ti = config.toolbar.items ;
45879             delete config.toolbar.items ;
45880         }
45881         
45882         var nitems = [];
45883         this.toolbar.render(tool_el);
45884         for(var i =0;i < ti.length;i++) {
45885           //  Roo.log(['add child', items[i]]);
45886             nitems.push(this.toolbar.addxtype(Roo.apply({}, ti[i])));
45887         }
45888         this.toolbar.items = nitems;
45889         
45890         delete config.toolbar;
45891     }
45892     
45893     Roo.bootstrap.panel.Grid.superclass.constructor.call(this, config);
45894     config.grid.scrollBody = true;;
45895     config.grid.monitorWindowResize = false; // turn off autosizing
45896     config.grid.autoHeight = false;
45897     config.grid.autoWidth = false;
45898     
45899     this.grid = new config.grid.xns[config.grid.xtype](config.grid);
45900     
45901     if (config.background) {
45902         // render grid on panel activation (if panel background)
45903         this.on('activate', function(gp) {
45904             if (!gp.grid.rendered) {
45905                 gp.grid.render(this.wrapper);
45906                 gp.grid.getGridEl().replaceClass("roo-layout-inactive-content", "roo-layout-component-panel");   
45907             }
45908         });
45909             
45910     } else {
45911         this.grid.render(this.wrapper);
45912         this.grid.getGridEl().replaceClass("roo-layout-inactive-content", "roo-layout-component-panel");               
45913
45914     }
45915     //this.wrapper.dom.appendChild(config.grid.getGridEl().dom);
45916     // ??? needed ??? config.el = this.wrapper;
45917     
45918     
45919     
45920   
45921     // xtype created footer. - not sure if will work as we normally have to render first..
45922     if (this.footer && !this.footer.el && this.footer.xtype) {
45923         
45924         var ctr = this.grid.getView().getFooterPanel(true);
45925         this.footer.dataSource = this.grid.dataSource;
45926         this.footer = Roo.factory(this.footer, Roo);
45927         this.footer.render(ctr);
45928         
45929     }
45930     
45931     
45932     
45933     
45934      
45935 };
45936
45937 Roo.extend(Roo.bootstrap.panel.Grid, Roo.bootstrap.panel.Content,
45938 {
45939   
45940     getId : function(){
45941         return this.grid.id;
45942     },
45943     
45944     /**
45945      * Returns the grid for this panel
45946      * @return {Roo.bootstrap.Table} 
45947      */
45948     getGrid : function(){
45949         return this.grid;    
45950     },
45951     
45952     setSize : function(width, height)
45953     {
45954      
45955         //if(!this.ignoreResize(width, height)){
45956             var grid = this.grid;
45957             var size = this.adjustForComponents(width, height);
45958             // tfoot is not a footer?
45959           
45960             
45961             var gridel = grid.getGridEl();
45962             gridel.setSize(size.width, size.height);
45963             
45964             var tbd = grid.getGridEl().select('tbody', true).first();
45965             var thd = grid.getGridEl().select('thead',true).first();
45966             var tbf= grid.getGridEl().select('tfoot', true).first();
45967
45968             if (tbf) {
45969                 size.height -= tbf.getHeight();
45970             }
45971             if (thd) {
45972                 size.height -= thd.getHeight();
45973             }
45974             
45975             tbd.setSize(size.width, size.height );
45976             // this is for the account management tab -seems to work there.
45977             var thd = grid.getGridEl().select('thead',true).first();
45978             //if (tbd) {
45979             //    tbd.setSize(size.width, size.height - thd.getHeight());
45980             //}
45981              
45982             grid.autoSize();
45983         //}
45984    
45985     },
45986      
45987     
45988     
45989     beforeSlide : function(){
45990         this.grid.getView().scroller.clip();
45991     },
45992     
45993     afterSlide : function(){
45994         this.grid.getView().scroller.unclip();
45995     },
45996     
45997     destroy : function(){
45998         this.grid.destroy();
45999         delete this.grid;
46000         Roo.bootstrap.panel.Grid.superclass.destroy.call(this); 
46001     }
46002 });
46003
46004 /**
46005  * @class Roo.bootstrap.panel.Nest
46006  * @extends Roo.bootstrap.panel.Content
46007  * @constructor
46008  * Create a new Panel, that can contain a layout.Border.
46009  * 
46010  * 
46011  * @param {String/Object} config A string to set only the title or a config object
46012  */
46013 Roo.bootstrap.panel.Nest = function(config)
46014 {
46015     // construct with only one argument..
46016     /* FIXME - implement nicer consturctors
46017     if (layout.layout) {
46018         config = layout;
46019         layout = config.layout;
46020         delete config.layout;
46021     }
46022     if (layout.xtype && !layout.getEl) {
46023         // then layout needs constructing..
46024         layout = Roo.factory(layout, Roo);
46025     }
46026     */
46027     
46028     config.el =  config.layout.getEl();
46029     
46030     Roo.bootstrap.panel.Nest.superclass.constructor.call(this, config);
46031     
46032     config.layout.monitorWindowResize = false; // turn off autosizing
46033     this.layout = config.layout;
46034     this.layout.getEl().addClass("roo-layout-nested-layout");
46035     this.layout.parent = this;
46036     
46037     
46038     
46039     
46040 };
46041
46042 Roo.extend(Roo.bootstrap.panel.Nest, Roo.bootstrap.panel.Content, {
46043     /**
46044     * @cfg {Roo.BorderLayout} layout The layout for this panel
46045     */
46046     layout : false,
46047
46048     setSize : function(width, height){
46049         if(!this.ignoreResize(width, height)){
46050             var size = this.adjustForComponents(width, height);
46051             var el = this.layout.getEl();
46052             if (size.height < 1) {
46053                 el.setWidth(size.width);   
46054             } else {
46055                 el.setSize(size.width, size.height);
46056             }
46057             var touch = el.dom.offsetWidth;
46058             this.layout.layout();
46059             // ie requires a double layout on the first pass
46060             if(Roo.isIE && !this.initialized){
46061                 this.initialized = true;
46062                 this.layout.layout();
46063             }
46064         }
46065     },
46066     
46067     // activate all subpanels if not currently active..
46068     
46069     setActiveState : function(active){
46070         this.active = active;
46071         this.setActiveClass(active);
46072         
46073         if(!active){
46074             this.fireEvent("deactivate", this);
46075             return;
46076         }
46077         
46078         this.fireEvent("activate", this);
46079         // not sure if this should happen before or after..
46080         if (!this.layout) {
46081             return; // should not happen..
46082         }
46083         var reg = false;
46084         for (var r in this.layout.regions) {
46085             reg = this.layout.getRegion(r);
46086             if (reg.getActivePanel()) {
46087                 //reg.showPanel(reg.getActivePanel()); // force it to activate.. 
46088                 reg.setActivePanel(reg.getActivePanel());
46089                 continue;
46090             }
46091             if (!reg.panels.length) {
46092                 continue;
46093             }
46094             reg.showPanel(reg.getPanel(0));
46095         }
46096         
46097         
46098         
46099         
46100     },
46101     
46102     /**
46103      * Returns the nested BorderLayout for this panel
46104      * @return {Roo.BorderLayout} 
46105      */
46106     getLayout : function(){
46107         return this.layout;
46108     },
46109     
46110      /**
46111      * Adds a xtype elements to the layout of the nested panel
46112      * <pre><code>
46113
46114 panel.addxtype({
46115        xtype : 'ContentPanel',
46116        region: 'west',
46117        items: [ .... ]
46118    }
46119 );
46120
46121 panel.addxtype({
46122         xtype : 'NestedLayoutPanel',
46123         region: 'west',
46124         layout: {
46125            center: { },
46126            west: { }   
46127         },
46128         items : [ ... list of content panels or nested layout panels.. ]
46129    }
46130 );
46131 </code></pre>
46132      * @param {Object} cfg Xtype definition of item to add.
46133      */
46134     addxtype : function(cfg) {
46135         return this.layout.addxtype(cfg);
46136     
46137     }
46138 });/*
46139  * Based on:
46140  * Ext JS Library 1.1.1
46141  * Copyright(c) 2006-2007, Ext JS, LLC.
46142  *
46143  * Originally Released Under LGPL - original licence link has changed is not relivant.
46144  *
46145  * Fork - LGPL
46146  * <script type="text/javascript">
46147  */
46148 /**
46149  * @class Roo.TabPanel
46150  * @extends Roo.util.Observable
46151  * A lightweight tab container.
46152  * <br><br>
46153  * Usage:
46154  * <pre><code>
46155 // basic tabs 1, built from existing content
46156 var tabs = new Roo.TabPanel("tabs1");
46157 tabs.addTab("script", "View Script");
46158 tabs.addTab("markup", "View Markup");
46159 tabs.activate("script");
46160
46161 // more advanced tabs, built from javascript
46162 var jtabs = new Roo.TabPanel("jtabs");
46163 jtabs.addTab("jtabs-1", "Normal Tab", "My content was added during construction.");
46164
46165 // set up the UpdateManager
46166 var tab2 = jtabs.addTab("jtabs-2", "Ajax Tab 1");
46167 var updater = tab2.getUpdateManager();
46168 updater.setDefaultUrl("ajax1.htm");
46169 tab2.on('activate', updater.refresh, updater, true);
46170
46171 // Use setUrl for Ajax loading
46172 var tab3 = jtabs.addTab("jtabs-3", "Ajax Tab 2");
46173 tab3.setUrl("ajax2.htm", null, true);
46174
46175 // Disabled tab
46176 var tab4 = jtabs.addTab("tabs1-5", "Disabled Tab", "Can't see me cause I'm disabled");
46177 tab4.disable();
46178
46179 jtabs.activate("jtabs-1");
46180  * </code></pre>
46181  * @constructor
46182  * Create a new TabPanel.
46183  * @param {String/HTMLElement/Roo.Element} container The id, DOM element or Roo.Element container where this TabPanel is to be rendered.
46184  * @param {Object/Boolean} config Config object to set any properties for this TabPanel, or true to render the tabs on the bottom.
46185  */
46186 Roo.bootstrap.panel.Tabs = function(config){
46187     /**
46188     * The container element for this TabPanel.
46189     * @type Roo.Element
46190     */
46191     this.el = Roo.get(config.el);
46192     delete config.el;
46193     if(config){
46194         if(typeof config == "boolean"){
46195             this.tabPosition = config ? "bottom" : "top";
46196         }else{
46197             Roo.apply(this, config);
46198         }
46199     }
46200     
46201     if(this.tabPosition == "bottom"){
46202         // if tabs are at the bottom = create the body first.
46203         this.bodyEl = Roo.get(this.createBody(this.el.dom));
46204         this.el.addClass("roo-tabs-bottom");
46205     }
46206     // next create the tabs holders
46207     
46208     if (this.tabPosition == "west"){
46209         
46210         var reg = this.region; // fake it..
46211         while (reg) {
46212             if (!reg.mgr.parent) {
46213                 break;
46214             }
46215             reg = reg.mgr.parent.region;
46216         }
46217         Roo.log("got nest?");
46218         Roo.log(reg);
46219         if (reg.mgr.getRegion('west')) {
46220             var ctrdom = reg.mgr.getRegion('west').bodyEl.dom;
46221             this.stripWrap = Roo.get(this.createStrip(ctrdom ), true);
46222             this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
46223             this.stripEl.setVisibilityMode(Roo.Element.DISPLAY);
46224             this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
46225         
46226             
46227         }
46228         
46229         
46230     } else {
46231      
46232         this.stripWrap = Roo.get(this.createStrip(this.el.dom), true);
46233         this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
46234         this.stripEl.setVisibilityMode(Roo.Element.DISPLAY);
46235         this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
46236     }
46237     
46238     
46239     if(Roo.isIE){
46240         Roo.fly(this.stripWrap.dom.firstChild).setStyle("overflow-x", "hidden");
46241     }
46242     
46243     // finally - if tabs are at the top, then create the body last..
46244     if(this.tabPosition != "bottom"){
46245         /** The body element that contains {@link Roo.TabPanelItem} bodies. +
46246          * @type Roo.Element
46247          */
46248         this.bodyEl = Roo.get(this.createBody(this.el.dom));
46249         this.el.addClass("roo-tabs-top");
46250     }
46251     this.items = [];
46252
46253     this.bodyEl.setStyle("position", "relative");
46254
46255     this.active = null;
46256     this.activateDelegate = this.activate.createDelegate(this);
46257
46258     this.addEvents({
46259         /**
46260          * @event tabchange
46261          * Fires when the active tab changes
46262          * @param {Roo.TabPanel} this
46263          * @param {Roo.TabPanelItem} activePanel The new active tab
46264          */
46265         "tabchange": true,
46266         /**
46267          * @event beforetabchange
46268          * Fires before the active tab changes, set cancel to true on the "e" parameter to cancel the change
46269          * @param {Roo.TabPanel} this
46270          * @param {Object} e Set cancel to true on this object to cancel the tab change
46271          * @param {Roo.TabPanelItem} tab The tab being changed to
46272          */
46273         "beforetabchange" : true
46274     });
46275
46276     Roo.EventManager.onWindowResize(this.onResize, this);
46277     this.cpad = this.el.getPadding("lr");
46278     this.hiddenCount = 0;
46279
46280
46281     // toolbar on the tabbar support...
46282     if (this.toolbar) {
46283         alert("no toolbar support yet");
46284         this.toolbar  = false;
46285         /*
46286         var tcfg = this.toolbar;
46287         tcfg.container = this.stripEl.child('td.x-tab-strip-toolbar');  
46288         this.toolbar = new Roo.Toolbar(tcfg);
46289         if (Roo.isSafari) {
46290             var tbl = tcfg.container.child('table', true);
46291             tbl.setAttribute('width', '100%');
46292         }
46293         */
46294         
46295     }
46296    
46297
46298
46299     Roo.bootstrap.panel.Tabs.superclass.constructor.call(this);
46300 };
46301
46302 Roo.extend(Roo.bootstrap.panel.Tabs, Roo.util.Observable, {
46303     /*
46304      *@cfg {String} tabPosition "top" or "bottom" (defaults to "top")
46305      */
46306     tabPosition : "top",
46307     /*
46308      *@cfg {Number} currentTabWidth The width of the current tab (defaults to 0)
46309      */
46310     currentTabWidth : 0,
46311     /*
46312      *@cfg {Number} minTabWidth The minimum width of a tab (defaults to 40) (ignored if {@link #resizeTabs} is not true)
46313      */
46314     minTabWidth : 40,
46315     /*
46316      *@cfg {Number} maxTabWidth The maximum width of a tab (defaults to 250) (ignored if {@link #resizeTabs} is not true)
46317      */
46318     maxTabWidth : 250,
46319     /*
46320      *@cfg {Number} preferredTabWidth The preferred (default) width of a tab (defaults to 175) (ignored if {@link #resizeTabs} is not true)
46321      */
46322     preferredTabWidth : 175,
46323     /*
46324      *@cfg {Boolean} resizeTabs True to enable dynamic tab resizing (defaults to false)
46325      */
46326     resizeTabs : false,
46327     /*
46328      *@cfg {Boolean} monitorResize Set this to true to turn on window resize monitoring (ignored if {@link #resizeTabs} is not true) (defaults to true)
46329      */
46330     monitorResize : true,
46331     /*
46332      *@cfg {Object} toolbar xtype description of toolbar to show at the right of the tab bar. 
46333      */
46334     toolbar : false,  // set by caller..
46335     
46336     region : false, /// set by caller
46337     
46338     disableTooltips : true, // not used yet...
46339
46340     /**
46341      * Creates a new {@link Roo.TabPanelItem} by looking for an existing element with the provided id -- if it's not found it creates one.
46342      * @param {String} id The id of the div to use <b>or create</b>
46343      * @param {String} text The text for the tab
46344      * @param {String} content (optional) Content to put in the TabPanelItem body
46345      * @param {Boolean} closable (optional) True to create a close icon on the tab
46346      * @return {Roo.TabPanelItem} The created TabPanelItem
46347      */
46348     addTab : function(id, text, content, closable, tpl)
46349     {
46350         var item = new Roo.bootstrap.panel.TabItem({
46351             panel: this,
46352             id : id,
46353             text : text,
46354             closable : closable,
46355             tpl : tpl
46356         });
46357         this.addTabItem(item);
46358         if(content){
46359             item.setContent(content);
46360         }
46361         return item;
46362     },
46363
46364     /**
46365      * Returns the {@link Roo.TabPanelItem} with the specified id/index
46366      * @param {String/Number} id The id or index of the TabPanelItem to fetch.
46367      * @return {Roo.TabPanelItem}
46368      */
46369     getTab : function(id){
46370         return this.items[id];
46371     },
46372
46373     /**
46374      * Hides the {@link Roo.TabPanelItem} with the specified id/index
46375      * @param {String/Number} id The id or index of the TabPanelItem to hide.
46376      */
46377     hideTab : function(id){
46378         var t = this.items[id];
46379         if(!t.isHidden()){
46380            t.setHidden(true);
46381            this.hiddenCount++;
46382            this.autoSizeTabs();
46383         }
46384     },
46385
46386     /**
46387      * "Unhides" the {@link Roo.TabPanelItem} with the specified id/index.
46388      * @param {String/Number} id The id or index of the TabPanelItem to unhide.
46389      */
46390     unhideTab : function(id){
46391         var t = this.items[id];
46392         if(t.isHidden()){
46393            t.setHidden(false);
46394            this.hiddenCount--;
46395            this.autoSizeTabs();
46396         }
46397     },
46398
46399     /**
46400      * Adds an existing {@link Roo.TabPanelItem}.
46401      * @param {Roo.TabPanelItem} item The TabPanelItem to add
46402      */
46403     addTabItem : function(item)
46404     {
46405         this.items[item.id] = item;
46406         this.items.push(item);
46407         this.autoSizeTabs();
46408       //  if(this.resizeTabs){
46409     //       item.setWidth(this.currentTabWidth || this.preferredTabWidth);
46410   //         this.autoSizeTabs();
46411 //        }else{
46412 //            item.autoSize();
46413        // }
46414     },
46415
46416     /**
46417      * Removes a {@link Roo.TabPanelItem}.
46418      * @param {String/Number} id The id or index of the TabPanelItem to remove.
46419      */
46420     removeTab : function(id){
46421         var items = this.items;
46422         var tab = items[id];
46423         if(!tab) { return; }
46424         var index = items.indexOf(tab);
46425         if(this.active == tab && items.length > 1){
46426             var newTab = this.getNextAvailable(index);
46427             if(newTab) {
46428                 newTab.activate();
46429             }
46430         }
46431         this.stripEl.dom.removeChild(tab.pnode.dom);
46432         if(tab.bodyEl.dom.parentNode == this.bodyEl.dom){ // if it was moved already prevent error
46433             this.bodyEl.dom.removeChild(tab.bodyEl.dom);
46434         }
46435         items.splice(index, 1);
46436         delete this.items[tab.id];
46437         tab.fireEvent("close", tab);
46438         tab.purgeListeners();
46439         this.autoSizeTabs();
46440     },
46441
46442     getNextAvailable : function(start){
46443         var items = this.items;
46444         var index = start;
46445         // look for a next tab that will slide over to
46446         // replace the one being removed
46447         while(index < items.length){
46448             var item = items[++index];
46449             if(item && !item.isHidden()){
46450                 return item;
46451             }
46452         }
46453         // if one isn't found select the previous tab (on the left)
46454         index = start;
46455         while(index >= 0){
46456             var item = items[--index];
46457             if(item && !item.isHidden()){
46458                 return item;
46459             }
46460         }
46461         return null;
46462     },
46463
46464     /**
46465      * Disables a {@link Roo.TabPanelItem}. It cannot be the active tab, if it is this call is ignored.
46466      * @param {String/Number} id The id or index of the TabPanelItem to disable.
46467      */
46468     disableTab : function(id){
46469         var tab = this.items[id];
46470         if(tab && this.active != tab){
46471             tab.disable();
46472         }
46473     },
46474
46475     /**
46476      * Enables a {@link Roo.TabPanelItem} that is disabled.
46477      * @param {String/Number} id The id or index of the TabPanelItem to enable.
46478      */
46479     enableTab : function(id){
46480         var tab = this.items[id];
46481         tab.enable();
46482     },
46483
46484     /**
46485      * Activates a {@link Roo.TabPanelItem}. The currently active one will be deactivated.
46486      * @param {String/Number} id The id or index of the TabPanelItem to activate.
46487      * @return {Roo.TabPanelItem} The TabPanelItem.
46488      */
46489     activate : function(id)
46490     {
46491         //Roo.log('activite:'  + id);
46492         
46493         var tab = this.items[id];
46494         if(!tab){
46495             return null;
46496         }
46497         if(tab == this.active || tab.disabled){
46498             return tab;
46499         }
46500         var e = {};
46501         this.fireEvent("beforetabchange", this, e, tab);
46502         if(e.cancel !== true && !tab.disabled){
46503             if(this.active){
46504                 this.active.hide();
46505             }
46506             this.active = this.items[id];
46507             this.active.show();
46508             this.fireEvent("tabchange", this, this.active);
46509         }
46510         return tab;
46511     },
46512
46513     /**
46514      * Gets the active {@link Roo.TabPanelItem}.
46515      * @return {Roo.TabPanelItem} The active TabPanelItem or null if none are active.
46516      */
46517     getActiveTab : function(){
46518         return this.active;
46519     },
46520
46521     /**
46522      * Updates the tab body element to fit the height of the container element
46523      * for overflow scrolling
46524      * @param {Number} targetHeight (optional) Override the starting height from the elements height
46525      */
46526     syncHeight : function(targetHeight){
46527         var height = (targetHeight || this.el.getHeight())-this.el.getBorderWidth("tb")-this.el.getPadding("tb");
46528         var bm = this.bodyEl.getMargins();
46529         var newHeight = height-(this.stripWrap.getHeight()||0)-(bm.top+bm.bottom);
46530         this.bodyEl.setHeight(newHeight);
46531         return newHeight;
46532     },
46533
46534     onResize : function(){
46535         if(this.monitorResize){
46536             this.autoSizeTabs();
46537         }
46538     },
46539
46540     /**
46541      * Disables tab resizing while tabs are being added (if {@link #resizeTabs} is false this does nothing)
46542      */
46543     beginUpdate : function(){
46544         this.updating = true;
46545     },
46546
46547     /**
46548      * Stops an update and resizes the tabs (if {@link #resizeTabs} is false this does nothing)
46549      */
46550     endUpdate : function(){
46551         this.updating = false;
46552         this.autoSizeTabs();
46553     },
46554
46555     /**
46556      * Manual call to resize the tabs (if {@link #resizeTabs} is false this does nothing)
46557      */
46558     autoSizeTabs : function()
46559     {
46560         var count = this.items.length;
46561         var vcount = count - this.hiddenCount;
46562         
46563         if (vcount < 2) {
46564             this.stripEl.hide();
46565         } else {
46566             this.stripEl.show();
46567         }
46568         
46569         if(!this.resizeTabs || count < 1 || vcount < 1 || this.updating) {
46570             return;
46571         }
46572         
46573         
46574         var w = Math.max(this.el.getWidth() - this.cpad, 10);
46575         var availWidth = Math.floor(w / vcount);
46576         var b = this.stripBody;
46577         if(b.getWidth() > w){
46578             var tabs = this.items;
46579             this.setTabWidth(Math.max(availWidth, this.minTabWidth)-2);
46580             if(availWidth < this.minTabWidth){
46581                 /*if(!this.sleft){    // incomplete scrolling code
46582                     this.createScrollButtons();
46583                 }
46584                 this.showScroll();
46585                 this.stripClip.setWidth(w - (this.sleft.getWidth()+this.sright.getWidth()));*/
46586             }
46587         }else{
46588             if(this.currentTabWidth < this.preferredTabWidth){
46589                 this.setTabWidth(Math.min(availWidth, this.preferredTabWidth)-2);
46590             }
46591         }
46592     },
46593
46594     /**
46595      * Returns the number of tabs in this TabPanel.
46596      * @return {Number}
46597      */
46598      getCount : function(){
46599          return this.items.length;
46600      },
46601
46602     /**
46603      * Resizes all the tabs to the passed width
46604      * @param {Number} The new width
46605      */
46606     setTabWidth : function(width){
46607         this.currentTabWidth = width;
46608         for(var i = 0, len = this.items.length; i < len; i++) {
46609                 if(!this.items[i].isHidden()) {
46610                 this.items[i].setWidth(width);
46611             }
46612         }
46613     },
46614
46615     /**
46616      * Destroys this TabPanel
46617      * @param {Boolean} removeEl (optional) True to remove the element from the DOM as well (defaults to undefined)
46618      */
46619     destroy : function(removeEl){
46620         Roo.EventManager.removeResizeListener(this.onResize, this);
46621         for(var i = 0, len = this.items.length; i < len; i++){
46622             this.items[i].purgeListeners();
46623         }
46624         if(removeEl === true){
46625             this.el.update("");
46626             this.el.remove();
46627         }
46628     },
46629     
46630     createStrip : function(container)
46631     {
46632         var strip = document.createElement("nav");
46633         strip.className = Roo.bootstrap.version == 4 ?
46634             "navbar-light bg-light" : 
46635             "navbar navbar-default"; //"x-tabs-wrap";
46636         container.appendChild(strip);
46637         return strip;
46638     },
46639     
46640     createStripList : function(strip)
46641     {
46642         // div wrapper for retard IE
46643         // returns the "tr" element.
46644         strip.innerHTML = '<ul class="nav nav-tabs" role="tablist"></ul>';
46645         //'<div class="x-tabs-strip-wrap">'+
46646           //  '<table class="x-tabs-strip" cellspacing="0" cellpadding="0" border="0"><tbody><tr>'+
46647           //  '<td class="x-tab-strip-toolbar"></td></tr></tbody></table></div>';
46648         return strip.firstChild; //.firstChild.firstChild.firstChild;
46649     },
46650     createBody : function(container)
46651     {
46652         var body = document.createElement("div");
46653         Roo.id(body, "tab-body");
46654         //Roo.fly(body).addClass("x-tabs-body");
46655         Roo.fly(body).addClass("tab-content");
46656         container.appendChild(body);
46657         return body;
46658     },
46659     createItemBody :function(bodyEl, id){
46660         var body = Roo.getDom(id);
46661         if(!body){
46662             body = document.createElement("div");
46663             body.id = id;
46664         }
46665         //Roo.fly(body).addClass("x-tabs-item-body");
46666         Roo.fly(body).addClass("tab-pane");
46667          bodyEl.insertBefore(body, bodyEl.firstChild);
46668         return body;
46669     },
46670     /** @private */
46671     createStripElements :  function(stripEl, text, closable, tpl)
46672     {
46673         var td = document.createElement("li"); // was td..
46674         td.className = 'nav-item';
46675         
46676         //stripEl.insertBefore(td, stripEl.childNodes[stripEl.childNodes.length-1]);
46677         
46678         
46679         stripEl.appendChild(td);
46680         /*if(closable){
46681             td.className = "x-tabs-closable";
46682             if(!this.closeTpl){
46683                 this.closeTpl = new Roo.Template(
46684                    '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
46685                    '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span>' +
46686                    '<div unselectable="on" class="close-icon">&#160;</div></em></span></a>'
46687                 );
46688             }
46689             var el = this.closeTpl.overwrite(td, {"text": text});
46690             var close = el.getElementsByTagName("div")[0];
46691             var inner = el.getElementsByTagName("em")[0];
46692             return {"el": el, "close": close, "inner": inner};
46693         } else {
46694         */
46695         // not sure what this is..
46696 //            if(!this.tabTpl){
46697                 //this.tabTpl = new Roo.Template(
46698                 //   '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
46699                 //   '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span></em></span></a>'
46700                 //);
46701 //                this.tabTpl = new Roo.Template(
46702 //                   '<a href="#">' +
46703 //                   '<span unselectable="on"' +
46704 //                            (this.disableTooltips ? '' : ' title="{text}"') +
46705 //                            ' >{text}</span></a>'
46706 //                );
46707 //                
46708 //            }
46709
46710
46711             var template = tpl || this.tabTpl || false;
46712             
46713             if(!template){
46714                 template =  new Roo.Template(
46715                         Roo.bootstrap.version == 4 ? 
46716                             (
46717                                 '<a class="nav-link" href="#" unselectable="on"' +
46718                                      (this.disableTooltips ? '' : ' title="{text}"') +
46719                                      ' >{text}</a>'
46720                             ) : (
46721                                 '<a class="nav-link" href="#">' +
46722                                 '<span unselectable="on"' +
46723                                          (this.disableTooltips ? '' : ' title="{text}"') +
46724                                     ' >{text}</span></a>'
46725                             )
46726                 );
46727             }
46728             
46729             switch (typeof(template)) {
46730                 case 'object' :
46731                     break;
46732                 case 'string' :
46733                     template = new Roo.Template(template);
46734                     break;
46735                 default :
46736                     break;
46737             }
46738             
46739             var el = template.overwrite(td, {"text": text});
46740             
46741             var inner = el.getElementsByTagName("span")[0];
46742             
46743             return {"el": el, "inner": inner};
46744             
46745     }
46746         
46747     
46748 });
46749
46750 /**
46751  * @class Roo.TabPanelItem
46752  * @extends Roo.util.Observable
46753  * Represents an individual item (tab plus body) in a TabPanel.
46754  * @param {Roo.TabPanel} tabPanel The {@link Roo.TabPanel} this TabPanelItem belongs to
46755  * @param {String} id The id of this TabPanelItem
46756  * @param {String} text The text for the tab of this TabPanelItem
46757  * @param {Boolean} closable True to allow this TabPanelItem to be closable (defaults to false)
46758  */
46759 Roo.bootstrap.panel.TabItem = function(config){
46760     /**
46761      * The {@link Roo.TabPanel} this TabPanelItem belongs to
46762      * @type Roo.TabPanel
46763      */
46764     this.tabPanel = config.panel;
46765     /**
46766      * The id for this TabPanelItem
46767      * @type String
46768      */
46769     this.id = config.id;
46770     /** @private */
46771     this.disabled = false;
46772     /** @private */
46773     this.text = config.text;
46774     /** @private */
46775     this.loaded = false;
46776     this.closable = config.closable;
46777
46778     /**
46779      * The body element for this TabPanelItem.
46780      * @type Roo.Element
46781      */
46782     this.bodyEl = Roo.get(this.tabPanel.createItemBody(this.tabPanel.bodyEl.dom, config.id));
46783     this.bodyEl.setVisibilityMode(Roo.Element.VISIBILITY);
46784     this.bodyEl.setStyle("display", "block");
46785     this.bodyEl.setStyle("zoom", "1");
46786     //this.hideAction();
46787
46788     var els = this.tabPanel.createStripElements(this.tabPanel.stripEl.dom, config.text, config.closable, config.tpl);
46789     /** @private */
46790     this.el = Roo.get(els.el);
46791     this.inner = Roo.get(els.inner, true);
46792      this.textEl = Roo.bootstrap.version == 4 ?
46793         this.el : Roo.get(this.el.dom.firstChild, true);
46794
46795     this.pnode = this.linode = Roo.get(els.el.parentNode, true);
46796     this.status_node = Roo.bootstrap.version == 4 ? this.el : this.linode;
46797
46798     
46799 //    this.el.on("mousedown", this.onTabMouseDown, this);
46800     this.el.on("click", this.onTabClick, this);
46801     /** @private */
46802     if(config.closable){
46803         var c = Roo.get(els.close, true);
46804         c.dom.title = this.closeText;
46805         c.addClassOnOver("close-over");
46806         c.on("click", this.closeClick, this);
46807      }
46808
46809     this.addEvents({
46810          /**
46811          * @event activate
46812          * Fires when this tab becomes the active tab.
46813          * @param {Roo.TabPanel} tabPanel The parent TabPanel
46814          * @param {Roo.TabPanelItem} this
46815          */
46816         "activate": true,
46817         /**
46818          * @event beforeclose
46819          * Fires before this tab is closed. To cancel the close, set cancel to true on e (e.cancel = true).
46820          * @param {Roo.TabPanelItem} this
46821          * @param {Object} e Set cancel to true on this object to cancel the close.
46822          */
46823         "beforeclose": true,
46824         /**
46825          * @event close
46826          * Fires when this tab is closed.
46827          * @param {Roo.TabPanelItem} this
46828          */
46829          "close": true,
46830         /**
46831          * @event deactivate
46832          * Fires when this tab is no longer the active tab.
46833          * @param {Roo.TabPanel} tabPanel The parent TabPanel
46834          * @param {Roo.TabPanelItem} this
46835          */
46836          "deactivate" : true
46837     });
46838     this.hidden = false;
46839
46840     Roo.bootstrap.panel.TabItem.superclass.constructor.call(this);
46841 };
46842
46843 Roo.extend(Roo.bootstrap.panel.TabItem, Roo.util.Observable,
46844            {
46845     purgeListeners : function(){
46846        Roo.util.Observable.prototype.purgeListeners.call(this);
46847        this.el.removeAllListeners();
46848     },
46849     /**
46850      * Shows this TabPanelItem -- this <b>does not</b> deactivate the currently active TabPanelItem.
46851      */
46852     show : function(){
46853         this.status_node.addClass("active");
46854         this.showAction();
46855         if(Roo.isOpera){
46856             this.tabPanel.stripWrap.repaint();
46857         }
46858         this.fireEvent("activate", this.tabPanel, this);
46859     },
46860
46861     /**
46862      * Returns true if this tab is the active tab.
46863      * @return {Boolean}
46864      */
46865     isActive : function(){
46866         return this.tabPanel.getActiveTab() == this;
46867     },
46868
46869     /**
46870      * Hides this TabPanelItem -- if you don't activate another TabPanelItem this could look odd.
46871      */
46872     hide : function(){
46873         this.status_node.removeClass("active");
46874         this.hideAction();
46875         this.fireEvent("deactivate", this.tabPanel, this);
46876     },
46877
46878     hideAction : function(){
46879         this.bodyEl.hide();
46880         this.bodyEl.setStyle("position", "absolute");
46881         this.bodyEl.setLeft("-20000px");
46882         this.bodyEl.setTop("-20000px");
46883     },
46884
46885     showAction : function(){
46886         this.bodyEl.setStyle("position", "relative");
46887         this.bodyEl.setTop("");
46888         this.bodyEl.setLeft("");
46889         this.bodyEl.show();
46890     },
46891
46892     /**
46893      * Set the tooltip for the tab.
46894      * @param {String} tooltip The tab's tooltip
46895      */
46896     setTooltip : function(text){
46897         if(Roo.QuickTips && Roo.QuickTips.isEnabled()){
46898             this.textEl.dom.qtip = text;
46899             this.textEl.dom.removeAttribute('title');
46900         }else{
46901             this.textEl.dom.title = text;
46902         }
46903     },
46904
46905     onTabClick : function(e){
46906         e.preventDefault();
46907         this.tabPanel.activate(this.id);
46908     },
46909
46910     onTabMouseDown : function(e){
46911         e.preventDefault();
46912         this.tabPanel.activate(this.id);
46913     },
46914 /*
46915     getWidth : function(){
46916         return this.inner.getWidth();
46917     },
46918
46919     setWidth : function(width){
46920         var iwidth = width - this.linode.getPadding("lr");
46921         this.inner.setWidth(iwidth);
46922         this.textEl.setWidth(iwidth-this.inner.getPadding("lr"));
46923         this.linode.setWidth(width);
46924     },
46925 */
46926     /**
46927      * Show or hide the tab
46928      * @param {Boolean} hidden True to hide or false to show.
46929      */
46930     setHidden : function(hidden){
46931         this.hidden = hidden;
46932         this.linode.setStyle("display", hidden ? "none" : "");
46933     },
46934
46935     /**
46936      * Returns true if this tab is "hidden"
46937      * @return {Boolean}
46938      */
46939     isHidden : function(){
46940         return this.hidden;
46941     },
46942
46943     /**
46944      * Returns the text for this tab
46945      * @return {String}
46946      */
46947     getText : function(){
46948         return this.text;
46949     },
46950     /*
46951     autoSize : function(){
46952         //this.el.beginMeasure();
46953         this.textEl.setWidth(1);
46954         /*
46955          *  #2804 [new] Tabs in Roojs
46956          *  increase the width by 2-4 pixels to prevent the ellipssis showing in chrome
46957          */
46958         //this.setWidth(this.textEl.dom.scrollWidth+this.linode.getPadding("lr")+this.inner.getPadding("lr") + 2);
46959         //this.el.endMeasure();
46960     //},
46961
46962     /**
46963      * Sets the text for the tab (Note: this also sets the tooltip text)
46964      * @param {String} text The tab's text and tooltip
46965      */
46966     setText : function(text){
46967         this.text = text;
46968         this.textEl.update(text);
46969         this.setTooltip(text);
46970         //if(!this.tabPanel.resizeTabs){
46971         //    this.autoSize();
46972         //}
46973     },
46974     /**
46975      * Activates this TabPanelItem -- this <b>does</b> deactivate the currently active TabPanelItem.
46976      */
46977     activate : function(){
46978         this.tabPanel.activate(this.id);
46979     },
46980
46981     /**
46982      * Disables this TabPanelItem -- this does nothing if this is the active TabPanelItem.
46983      */
46984     disable : function(){
46985         if(this.tabPanel.active != this){
46986             this.disabled = true;
46987             this.status_node.addClass("disabled");
46988         }
46989     },
46990
46991     /**
46992      * Enables this TabPanelItem if it was previously disabled.
46993      */
46994     enable : function(){
46995         this.disabled = false;
46996         this.status_node.removeClass("disabled");
46997     },
46998
46999     /**
47000      * Sets the content for this TabPanelItem.
47001      * @param {String} content The content
47002      * @param {Boolean} loadScripts true to look for and load scripts
47003      */
47004     setContent : function(content, loadScripts){
47005         this.bodyEl.update(content, loadScripts);
47006     },
47007
47008     /**
47009      * Gets the {@link Roo.UpdateManager} for the body of this TabPanelItem. Enables you to perform Ajax updates.
47010      * @return {Roo.UpdateManager} The UpdateManager
47011      */
47012     getUpdateManager : function(){
47013         return this.bodyEl.getUpdateManager();
47014     },
47015
47016     /**
47017      * Set a URL to be used to load the content for this TabPanelItem.
47018      * @param {String/Function} url The URL to load the content from, or a function to call to get the URL
47019      * @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)
47020      * @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)
47021      * @return {Roo.UpdateManager} The UpdateManager
47022      */
47023     setUrl : function(url, params, loadOnce){
47024         if(this.refreshDelegate){
47025             this.un('activate', this.refreshDelegate);
47026         }
47027         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
47028         this.on("activate", this.refreshDelegate);
47029         return this.bodyEl.getUpdateManager();
47030     },
47031
47032     /** @private */
47033     _handleRefresh : function(url, params, loadOnce){
47034         if(!loadOnce || !this.loaded){
47035             var updater = this.bodyEl.getUpdateManager();
47036             updater.update(url, params, this._setLoaded.createDelegate(this));
47037         }
47038     },
47039
47040     /**
47041      *   Forces a content refresh from the URL specified in the {@link #setUrl} method.
47042      *   Will fail silently if the setUrl method has not been called.
47043      *   This does not activate the panel, just updates its content.
47044      */
47045     refresh : function(){
47046         if(this.refreshDelegate){
47047            this.loaded = false;
47048            this.refreshDelegate();
47049         }
47050     },
47051
47052     /** @private */
47053     _setLoaded : function(){
47054         this.loaded = true;
47055     },
47056
47057     /** @private */
47058     closeClick : function(e){
47059         var o = {};
47060         e.stopEvent();
47061         this.fireEvent("beforeclose", this, o);
47062         if(o.cancel !== true){
47063             this.tabPanel.removeTab(this.id);
47064         }
47065     },
47066     /**
47067      * The text displayed in the tooltip for the close icon.
47068      * @type String
47069      */
47070     closeText : "Close this tab"
47071 });
47072 /**
47073 *    This script refer to:
47074 *    Title: International Telephone Input
47075 *    Author: Jack O'Connor
47076 *    Code version:  v12.1.12
47077 *    Availability: https://github.com/jackocnr/intl-tel-input.git
47078 **/
47079
47080 Roo.bootstrap.form.PhoneInputData = function() {
47081     var d = [
47082       [
47083         "Afghanistan (‫افغانستان‬‎)",
47084         "af",
47085         "93"
47086       ],
47087       [
47088         "Albania (Shqipëri)",
47089         "al",
47090         "355"
47091       ],
47092       [
47093         "Algeria (‫الجزائر‬‎)",
47094         "dz",
47095         "213"
47096       ],
47097       [
47098         "American Samoa",
47099         "as",
47100         "1684"
47101       ],
47102       [
47103         "Andorra",
47104         "ad",
47105         "376"
47106       ],
47107       [
47108         "Angola",
47109         "ao",
47110         "244"
47111       ],
47112       [
47113         "Anguilla",
47114         "ai",
47115         "1264"
47116       ],
47117       [
47118         "Antigua and Barbuda",
47119         "ag",
47120         "1268"
47121       ],
47122       [
47123         "Argentina",
47124         "ar",
47125         "54"
47126       ],
47127       [
47128         "Armenia (Հայաստան)",
47129         "am",
47130         "374"
47131       ],
47132       [
47133         "Aruba",
47134         "aw",
47135         "297"
47136       ],
47137       [
47138         "Australia",
47139         "au",
47140         "61",
47141         0
47142       ],
47143       [
47144         "Austria (Österreich)",
47145         "at",
47146         "43"
47147       ],
47148       [
47149         "Azerbaijan (Azərbaycan)",
47150         "az",
47151         "994"
47152       ],
47153       [
47154         "Bahamas",
47155         "bs",
47156         "1242"
47157       ],
47158       [
47159         "Bahrain (‫البحرين‬‎)",
47160         "bh",
47161         "973"
47162       ],
47163       [
47164         "Bangladesh (বাংলাদেশ)",
47165         "bd",
47166         "880"
47167       ],
47168       [
47169         "Barbados",
47170         "bb",
47171         "1246"
47172       ],
47173       [
47174         "Belarus (Беларусь)",
47175         "by",
47176         "375"
47177       ],
47178       [
47179         "Belgium (België)",
47180         "be",
47181         "32"
47182       ],
47183       [
47184         "Belize",
47185         "bz",
47186         "501"
47187       ],
47188       [
47189         "Benin (Bénin)",
47190         "bj",
47191         "229"
47192       ],
47193       [
47194         "Bermuda",
47195         "bm",
47196         "1441"
47197       ],
47198       [
47199         "Bhutan (འབྲུག)",
47200         "bt",
47201         "975"
47202       ],
47203       [
47204         "Bolivia",
47205         "bo",
47206         "591"
47207       ],
47208       [
47209         "Bosnia and Herzegovina (Босна и Херцеговина)",
47210         "ba",
47211         "387"
47212       ],
47213       [
47214         "Botswana",
47215         "bw",
47216         "267"
47217       ],
47218       [
47219         "Brazil (Brasil)",
47220         "br",
47221         "55"
47222       ],
47223       [
47224         "British Indian Ocean Territory",
47225         "io",
47226         "246"
47227       ],
47228       [
47229         "British Virgin Islands",
47230         "vg",
47231         "1284"
47232       ],
47233       [
47234         "Brunei",
47235         "bn",
47236         "673"
47237       ],
47238       [
47239         "Bulgaria (България)",
47240         "bg",
47241         "359"
47242       ],
47243       [
47244         "Burkina Faso",
47245         "bf",
47246         "226"
47247       ],
47248       [
47249         "Burundi (Uburundi)",
47250         "bi",
47251         "257"
47252       ],
47253       [
47254         "Cambodia (កម្ពុជា)",
47255         "kh",
47256         "855"
47257       ],
47258       [
47259         "Cameroon (Cameroun)",
47260         "cm",
47261         "237"
47262       ],
47263       [
47264         "Canada",
47265         "ca",
47266         "1",
47267         1,
47268         ["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"]
47269       ],
47270       [
47271         "Cape Verde (Kabu Verdi)",
47272         "cv",
47273         "238"
47274       ],
47275       [
47276         "Caribbean Netherlands",
47277         "bq",
47278         "599",
47279         1
47280       ],
47281       [
47282         "Cayman Islands",
47283         "ky",
47284         "1345"
47285       ],
47286       [
47287         "Central African Republic (République centrafricaine)",
47288         "cf",
47289         "236"
47290       ],
47291       [
47292         "Chad (Tchad)",
47293         "td",
47294         "235"
47295       ],
47296       [
47297         "Chile",
47298         "cl",
47299         "56"
47300       ],
47301       [
47302         "China (中国)",
47303         "cn",
47304         "86"
47305       ],
47306       [
47307         "Christmas Island",
47308         "cx",
47309         "61",
47310         2
47311       ],
47312       [
47313         "Cocos (Keeling) Islands",
47314         "cc",
47315         "61",
47316         1
47317       ],
47318       [
47319         "Colombia",
47320         "co",
47321         "57"
47322       ],
47323       [
47324         "Comoros (‫جزر القمر‬‎)",
47325         "km",
47326         "269"
47327       ],
47328       [
47329         "Congo (DRC) (Jamhuri ya Kidemokrasia ya Kongo)",
47330         "cd",
47331         "243"
47332       ],
47333       [
47334         "Congo (Republic) (Congo-Brazzaville)",
47335         "cg",
47336         "242"
47337       ],
47338       [
47339         "Cook Islands",
47340         "ck",
47341         "682"
47342       ],
47343       [
47344         "Costa Rica",
47345         "cr",
47346         "506"
47347       ],
47348       [
47349         "Côte d’Ivoire",
47350         "ci",
47351         "225"
47352       ],
47353       [
47354         "Croatia (Hrvatska)",
47355         "hr",
47356         "385"
47357       ],
47358       [
47359         "Cuba",
47360         "cu",
47361         "53"
47362       ],
47363       [
47364         "Curaçao",
47365         "cw",
47366         "599",
47367         0
47368       ],
47369       [
47370         "Cyprus (Κύπρος)",
47371         "cy",
47372         "357"
47373       ],
47374       [
47375         "Czech Republic (Česká republika)",
47376         "cz",
47377         "420"
47378       ],
47379       [
47380         "Denmark (Danmark)",
47381         "dk",
47382         "45"
47383       ],
47384       [
47385         "Djibouti",
47386         "dj",
47387         "253"
47388       ],
47389       [
47390         "Dominica",
47391         "dm",
47392         "1767"
47393       ],
47394       [
47395         "Dominican Republic (República Dominicana)",
47396         "do",
47397         "1",
47398         2,
47399         ["809", "829", "849"]
47400       ],
47401       [
47402         "Ecuador",
47403         "ec",
47404         "593"
47405       ],
47406       [
47407         "Egypt (‫مصر‬‎)",
47408         "eg",
47409         "20"
47410       ],
47411       [
47412         "El Salvador",
47413         "sv",
47414         "503"
47415       ],
47416       [
47417         "Equatorial Guinea (Guinea Ecuatorial)",
47418         "gq",
47419         "240"
47420       ],
47421       [
47422         "Eritrea",
47423         "er",
47424         "291"
47425       ],
47426       [
47427         "Estonia (Eesti)",
47428         "ee",
47429         "372"
47430       ],
47431       [
47432         "Ethiopia",
47433         "et",
47434         "251"
47435       ],
47436       [
47437         "Falkland Islands (Islas Malvinas)",
47438         "fk",
47439         "500"
47440       ],
47441       [
47442         "Faroe Islands (Føroyar)",
47443         "fo",
47444         "298"
47445       ],
47446       [
47447         "Fiji",
47448         "fj",
47449         "679"
47450       ],
47451       [
47452         "Finland (Suomi)",
47453         "fi",
47454         "358",
47455         0
47456       ],
47457       [
47458         "France",
47459         "fr",
47460         "33"
47461       ],
47462       [
47463         "French Guiana (Guyane française)",
47464         "gf",
47465         "594"
47466       ],
47467       [
47468         "French Polynesia (Polynésie française)",
47469         "pf",
47470         "689"
47471       ],
47472       [
47473         "Gabon",
47474         "ga",
47475         "241"
47476       ],
47477       [
47478         "Gambia",
47479         "gm",
47480         "220"
47481       ],
47482       [
47483         "Georgia (საქართველო)",
47484         "ge",
47485         "995"
47486       ],
47487       [
47488         "Germany (Deutschland)",
47489         "de",
47490         "49"
47491       ],
47492       [
47493         "Ghana (Gaana)",
47494         "gh",
47495         "233"
47496       ],
47497       [
47498         "Gibraltar",
47499         "gi",
47500         "350"
47501       ],
47502       [
47503         "Greece (Ελλάδα)",
47504         "gr",
47505         "30"
47506       ],
47507       [
47508         "Greenland (Kalaallit Nunaat)",
47509         "gl",
47510         "299"
47511       ],
47512       [
47513         "Grenada",
47514         "gd",
47515         "1473"
47516       ],
47517       [
47518         "Guadeloupe",
47519         "gp",
47520         "590",
47521         0
47522       ],
47523       [
47524         "Guam",
47525         "gu",
47526         "1671"
47527       ],
47528       [
47529         "Guatemala",
47530         "gt",
47531         "502"
47532       ],
47533       [
47534         "Guernsey",
47535         "gg",
47536         "44",
47537         1
47538       ],
47539       [
47540         "Guinea (Guinée)",
47541         "gn",
47542         "224"
47543       ],
47544       [
47545         "Guinea-Bissau (Guiné Bissau)",
47546         "gw",
47547         "245"
47548       ],
47549       [
47550         "Guyana",
47551         "gy",
47552         "592"
47553       ],
47554       [
47555         "Haiti",
47556         "ht",
47557         "509"
47558       ],
47559       [
47560         "Honduras",
47561         "hn",
47562         "504"
47563       ],
47564       [
47565         "Hong Kong (香港)",
47566         "hk",
47567         "852"
47568       ],
47569       [
47570         "Hungary (Magyarország)",
47571         "hu",
47572         "36"
47573       ],
47574       [
47575         "Iceland (Ísland)",
47576         "is",
47577         "354"
47578       ],
47579       [
47580         "India (भारत)",
47581         "in",
47582         "91"
47583       ],
47584       [
47585         "Indonesia",
47586         "id",
47587         "62"
47588       ],
47589       [
47590         "Iran (‫ایران‬‎)",
47591         "ir",
47592         "98"
47593       ],
47594       [
47595         "Iraq (‫العراق‬‎)",
47596         "iq",
47597         "964"
47598       ],
47599       [
47600         "Ireland",
47601         "ie",
47602         "353"
47603       ],
47604       [
47605         "Isle of Man",
47606         "im",
47607         "44",
47608         2
47609       ],
47610       [
47611         "Israel (‫ישראל‬‎)",
47612         "il",
47613         "972"
47614       ],
47615       [
47616         "Italy (Italia)",
47617         "it",
47618         "39",
47619         0
47620       ],
47621       [
47622         "Jamaica",
47623         "jm",
47624         "1876"
47625       ],
47626       [
47627         "Japan (日本)",
47628         "jp",
47629         "81"
47630       ],
47631       [
47632         "Jersey",
47633         "je",
47634         "44",
47635         3
47636       ],
47637       [
47638         "Jordan (‫الأردن‬‎)",
47639         "jo",
47640         "962"
47641       ],
47642       [
47643         "Kazakhstan (Казахстан)",
47644         "kz",
47645         "7",
47646         1
47647       ],
47648       [
47649         "Kenya",
47650         "ke",
47651         "254"
47652       ],
47653       [
47654         "Kiribati",
47655         "ki",
47656         "686"
47657       ],
47658       [
47659         "Kosovo",
47660         "xk",
47661         "383"
47662       ],
47663       [
47664         "Kuwait (‫الكويت‬‎)",
47665         "kw",
47666         "965"
47667       ],
47668       [
47669         "Kyrgyzstan (Кыргызстан)",
47670         "kg",
47671         "996"
47672       ],
47673       [
47674         "Laos (ລາວ)",
47675         "la",
47676         "856"
47677       ],
47678       [
47679         "Latvia (Latvija)",
47680         "lv",
47681         "371"
47682       ],
47683       [
47684         "Lebanon (‫لبنان‬‎)",
47685         "lb",
47686         "961"
47687       ],
47688       [
47689         "Lesotho",
47690         "ls",
47691         "266"
47692       ],
47693       [
47694         "Liberia",
47695         "lr",
47696         "231"
47697       ],
47698       [
47699         "Libya (‫ليبيا‬‎)",
47700         "ly",
47701         "218"
47702       ],
47703       [
47704         "Liechtenstein",
47705         "li",
47706         "423"
47707       ],
47708       [
47709         "Lithuania (Lietuva)",
47710         "lt",
47711         "370"
47712       ],
47713       [
47714         "Luxembourg",
47715         "lu",
47716         "352"
47717       ],
47718       [
47719         "Macau (澳門)",
47720         "mo",
47721         "853"
47722       ],
47723       [
47724         "Macedonia (FYROM) (Македонија)",
47725         "mk",
47726         "389"
47727       ],
47728       [
47729         "Madagascar (Madagasikara)",
47730         "mg",
47731         "261"
47732       ],
47733       [
47734         "Malawi",
47735         "mw",
47736         "265"
47737       ],
47738       [
47739         "Malaysia",
47740         "my",
47741         "60"
47742       ],
47743       [
47744         "Maldives",
47745         "mv",
47746         "960"
47747       ],
47748       [
47749         "Mali",
47750         "ml",
47751         "223"
47752       ],
47753       [
47754         "Malta",
47755         "mt",
47756         "356"
47757       ],
47758       [
47759         "Marshall Islands",
47760         "mh",
47761         "692"
47762       ],
47763       [
47764         "Martinique",
47765         "mq",
47766         "596"
47767       ],
47768       [
47769         "Mauritania (‫موريتانيا‬‎)",
47770         "mr",
47771         "222"
47772       ],
47773       [
47774         "Mauritius (Moris)",
47775         "mu",
47776         "230"
47777       ],
47778       [
47779         "Mayotte",
47780         "yt",
47781         "262",
47782         1
47783       ],
47784       [
47785         "Mexico (México)",
47786         "mx",
47787         "52"
47788       ],
47789       [
47790         "Micronesia",
47791         "fm",
47792         "691"
47793       ],
47794       [
47795         "Moldova (Republica Moldova)",
47796         "md",
47797         "373"
47798       ],
47799       [
47800         "Monaco",
47801         "mc",
47802         "377"
47803       ],
47804       [
47805         "Mongolia (Монгол)",
47806         "mn",
47807         "976"
47808       ],
47809       [
47810         "Montenegro (Crna Gora)",
47811         "me",
47812         "382"
47813       ],
47814       [
47815         "Montserrat",
47816         "ms",
47817         "1664"
47818       ],
47819       [
47820         "Morocco (‫المغرب‬‎)",
47821         "ma",
47822         "212",
47823         0
47824       ],
47825       [
47826         "Mozambique (Moçambique)",
47827         "mz",
47828         "258"
47829       ],
47830       [
47831         "Myanmar (Burma) (မြန်မာ)",
47832         "mm",
47833         "95"
47834       ],
47835       [
47836         "Namibia (Namibië)",
47837         "na",
47838         "264"
47839       ],
47840       [
47841         "Nauru",
47842         "nr",
47843         "674"
47844       ],
47845       [
47846         "Nepal (नेपाल)",
47847         "np",
47848         "977"
47849       ],
47850       [
47851         "Netherlands (Nederland)",
47852         "nl",
47853         "31"
47854       ],
47855       [
47856         "New Caledonia (Nouvelle-Calédonie)",
47857         "nc",
47858         "687"
47859       ],
47860       [
47861         "New Zealand",
47862         "nz",
47863         "64"
47864       ],
47865       [
47866         "Nicaragua",
47867         "ni",
47868         "505"
47869       ],
47870       [
47871         "Niger (Nijar)",
47872         "ne",
47873         "227"
47874       ],
47875       [
47876         "Nigeria",
47877         "ng",
47878         "234"
47879       ],
47880       [
47881         "Niue",
47882         "nu",
47883         "683"
47884       ],
47885       [
47886         "Norfolk Island",
47887         "nf",
47888         "672"
47889       ],
47890       [
47891         "North Korea (조선 민주주의 인민 공화국)",
47892         "kp",
47893         "850"
47894       ],
47895       [
47896         "Northern Mariana Islands",
47897         "mp",
47898         "1670"
47899       ],
47900       [
47901         "Norway (Norge)",
47902         "no",
47903         "47",
47904         0
47905       ],
47906       [
47907         "Oman (‫عُمان‬‎)",
47908         "om",
47909         "968"
47910       ],
47911       [
47912         "Pakistan (‫پاکستان‬‎)",
47913         "pk",
47914         "92"
47915       ],
47916       [
47917         "Palau",
47918         "pw",
47919         "680"
47920       ],
47921       [
47922         "Palestine (‫فلسطين‬‎)",
47923         "ps",
47924         "970"
47925       ],
47926       [
47927         "Panama (Panamá)",
47928         "pa",
47929         "507"
47930       ],
47931       [
47932         "Papua New Guinea",
47933         "pg",
47934         "675"
47935       ],
47936       [
47937         "Paraguay",
47938         "py",
47939         "595"
47940       ],
47941       [
47942         "Peru (Perú)",
47943         "pe",
47944         "51"
47945       ],
47946       [
47947         "Philippines",
47948         "ph",
47949         "63"
47950       ],
47951       [
47952         "Poland (Polska)",
47953         "pl",
47954         "48"
47955       ],
47956       [
47957         "Portugal",
47958         "pt",
47959         "351"
47960       ],
47961       [
47962         "Puerto Rico",
47963         "pr",
47964         "1",
47965         3,
47966         ["787", "939"]
47967       ],
47968       [
47969         "Qatar (‫قطر‬‎)",
47970         "qa",
47971         "974"
47972       ],
47973       [
47974         "Réunion (La Réunion)",
47975         "re",
47976         "262",
47977         0
47978       ],
47979       [
47980         "Romania (România)",
47981         "ro",
47982         "40"
47983       ],
47984       [
47985         "Russia (Россия)",
47986         "ru",
47987         "7",
47988         0
47989       ],
47990       [
47991         "Rwanda",
47992         "rw",
47993         "250"
47994       ],
47995       [
47996         "Saint Barthélemy",
47997         "bl",
47998         "590",
47999         1
48000       ],
48001       [
48002         "Saint Helena",
48003         "sh",
48004         "290"
48005       ],
48006       [
48007         "Saint Kitts and Nevis",
48008         "kn",
48009         "1869"
48010       ],
48011       [
48012         "Saint Lucia",
48013         "lc",
48014         "1758"
48015       ],
48016       [
48017         "Saint Martin (Saint-Martin (partie française))",
48018         "mf",
48019         "590",
48020         2
48021       ],
48022       [
48023         "Saint Pierre and Miquelon (Saint-Pierre-et-Miquelon)",
48024         "pm",
48025         "508"
48026       ],
48027       [
48028         "Saint Vincent and the Grenadines",
48029         "vc",
48030         "1784"
48031       ],
48032       [
48033         "Samoa",
48034         "ws",
48035         "685"
48036       ],
48037       [
48038         "San Marino",
48039         "sm",
48040         "378"
48041       ],
48042       [
48043         "São Tomé and Príncipe (São Tomé e Príncipe)",
48044         "st",
48045         "239"
48046       ],
48047       [
48048         "Saudi Arabia (‫المملكة العربية السعودية‬‎)",
48049         "sa",
48050         "966"
48051       ],
48052       [
48053         "Senegal (Sénégal)",
48054         "sn",
48055         "221"
48056       ],
48057       [
48058         "Serbia (Србија)",
48059         "rs",
48060         "381"
48061       ],
48062       [
48063         "Seychelles",
48064         "sc",
48065         "248"
48066       ],
48067       [
48068         "Sierra Leone",
48069         "sl",
48070         "232"
48071       ],
48072       [
48073         "Singapore",
48074         "sg",
48075         "65"
48076       ],
48077       [
48078         "Sint Maarten",
48079         "sx",
48080         "1721"
48081       ],
48082       [
48083         "Slovakia (Slovensko)",
48084         "sk",
48085         "421"
48086       ],
48087       [
48088         "Slovenia (Slovenija)",
48089         "si",
48090         "386"
48091       ],
48092       [
48093         "Solomon Islands",
48094         "sb",
48095         "677"
48096       ],
48097       [
48098         "Somalia (Soomaaliya)",
48099         "so",
48100         "252"
48101       ],
48102       [
48103         "South Africa",
48104         "za",
48105         "27"
48106       ],
48107       [
48108         "South Korea (대한민국)",
48109         "kr",
48110         "82"
48111       ],
48112       [
48113         "South Sudan (‫جنوب السودان‬‎)",
48114         "ss",
48115         "211"
48116       ],
48117       [
48118         "Spain (España)",
48119         "es",
48120         "34"
48121       ],
48122       [
48123         "Sri Lanka (ශ්‍රී ලංකාව)",
48124         "lk",
48125         "94"
48126       ],
48127       [
48128         "Sudan (‫السودان‬‎)",
48129         "sd",
48130         "249"
48131       ],
48132       [
48133         "Suriname",
48134         "sr",
48135         "597"
48136       ],
48137       [
48138         "Svalbard and Jan Mayen",
48139         "sj",
48140         "47",
48141         1
48142       ],
48143       [
48144         "Swaziland",
48145         "sz",
48146         "268"
48147       ],
48148       [
48149         "Sweden (Sverige)",
48150         "se",
48151         "46"
48152       ],
48153       [
48154         "Switzerland (Schweiz)",
48155         "ch",
48156         "41"
48157       ],
48158       [
48159         "Syria (‫سوريا‬‎)",
48160         "sy",
48161         "963"
48162       ],
48163       [
48164         "Taiwan (台灣)",
48165         "tw",
48166         "886"
48167       ],
48168       [
48169         "Tajikistan",
48170         "tj",
48171         "992"
48172       ],
48173       [
48174         "Tanzania",
48175         "tz",
48176         "255"
48177       ],
48178       [
48179         "Thailand (ไทย)",
48180         "th",
48181         "66"
48182       ],
48183       [
48184         "Timor-Leste",
48185         "tl",
48186         "670"
48187       ],
48188       [
48189         "Togo",
48190         "tg",
48191         "228"
48192       ],
48193       [
48194         "Tokelau",
48195         "tk",
48196         "690"
48197       ],
48198       [
48199         "Tonga",
48200         "to",
48201         "676"
48202       ],
48203       [
48204         "Trinidad and Tobago",
48205         "tt",
48206         "1868"
48207       ],
48208       [
48209         "Tunisia (‫تونس‬‎)",
48210         "tn",
48211         "216"
48212       ],
48213       [
48214         "Turkey (Türkiye)",
48215         "tr",
48216         "90"
48217       ],
48218       [
48219         "Turkmenistan",
48220         "tm",
48221         "993"
48222       ],
48223       [
48224         "Turks and Caicos Islands",
48225         "tc",
48226         "1649"
48227       ],
48228       [
48229         "Tuvalu",
48230         "tv",
48231         "688"
48232       ],
48233       [
48234         "U.S. Virgin Islands",
48235         "vi",
48236         "1340"
48237       ],
48238       [
48239         "Uganda",
48240         "ug",
48241         "256"
48242       ],
48243       [
48244         "Ukraine (Україна)",
48245         "ua",
48246         "380"
48247       ],
48248       [
48249         "United Arab Emirates (‫الإمارات العربية المتحدة‬‎)",
48250         "ae",
48251         "971"
48252       ],
48253       [
48254         "United Kingdom",
48255         "gb",
48256         "44",
48257         0
48258       ],
48259       [
48260         "United States",
48261         "us",
48262         "1",
48263         0
48264       ],
48265       [
48266         "Uruguay",
48267         "uy",
48268         "598"
48269       ],
48270       [
48271         "Uzbekistan (Oʻzbekiston)",
48272         "uz",
48273         "998"
48274       ],
48275       [
48276         "Vanuatu",
48277         "vu",
48278         "678"
48279       ],
48280       [
48281         "Vatican City (Città del Vaticano)",
48282         "va",
48283         "39",
48284         1
48285       ],
48286       [
48287         "Venezuela",
48288         "ve",
48289         "58"
48290       ],
48291       [
48292         "Vietnam (Việt Nam)",
48293         "vn",
48294         "84"
48295       ],
48296       [
48297         "Wallis and Futuna (Wallis-et-Futuna)",
48298         "wf",
48299         "681"
48300       ],
48301       [
48302         "Western Sahara (‫الصحراء الغربية‬‎)",
48303         "eh",
48304         "212",
48305         1
48306       ],
48307       [
48308         "Yemen (‫اليمن‬‎)",
48309         "ye",
48310         "967"
48311       ],
48312       [
48313         "Zambia",
48314         "zm",
48315         "260"
48316       ],
48317       [
48318         "Zimbabwe",
48319         "zw",
48320         "263"
48321       ],
48322       [
48323         "Åland Islands",
48324         "ax",
48325         "358",
48326         1
48327       ]
48328   ];
48329   
48330   return d;
48331 }/**
48332 *    This script refer to:
48333 *    Title: International Telephone Input
48334 *    Author: Jack O'Connor
48335 *    Code version:  v12.1.12
48336 *    Availability: https://github.com/jackocnr/intl-tel-input.git
48337 **/
48338
48339 /**
48340  * @class Roo.bootstrap.form.PhoneInput
48341  * @extends Roo.bootstrap.form.TriggerField
48342  * An input with International dial-code selection
48343  
48344  * @cfg {String} defaultDialCode default '+852'
48345  * @cfg {Array} preferedCountries default []
48346   
48347  * @constructor
48348  * Create a new PhoneInput.
48349  * @param {Object} config Configuration options
48350  */
48351
48352 Roo.bootstrap.form.PhoneInput = function(config) {
48353     Roo.bootstrap.form.PhoneInput.superclass.constructor.call(this, config);
48354 };
48355
48356 Roo.extend(Roo.bootstrap.form.PhoneInput, Roo.bootstrap.form.TriggerField, {
48357         /**
48358         * @cfg {Roo.data.Store} store [required] The data store to which this combo is bound (defaults to undefined)
48359         */
48360         listWidth: undefined,
48361         
48362         selectedClass: 'active',
48363         
48364         invalidClass : "has-warning",
48365         
48366         validClass: 'has-success',
48367         
48368         allowed: '0123456789',
48369         
48370         max_length: 15,
48371         
48372         /**
48373          * @cfg {String} defaultDialCode The default dial code when initializing the input
48374          */
48375         defaultDialCode: '+852',
48376         
48377         /**
48378          * @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
48379          */
48380         preferedCountries: false,
48381         
48382         getAutoCreate : function()
48383         {
48384             var data = Roo.bootstrap.form.PhoneInputData();
48385             var align = this.labelAlign || this.parentLabelAlign();
48386             var id = Roo.id();
48387             
48388             this.allCountries = [];
48389             this.dialCodeMapping = [];
48390             
48391             for (var i = 0; i < data.length; i++) {
48392               var c = data[i];
48393               this.allCountries[i] = {
48394                 name: c[0],
48395                 iso2: c[1],
48396                 dialCode: c[2],
48397                 priority: c[3] || 0,
48398                 areaCodes: c[4] || null
48399               };
48400               this.dialCodeMapping[c[2]] = {
48401                   name: c[0],
48402                   iso2: c[1],
48403                   priority: c[3] || 0,
48404                   areaCodes: c[4] || null
48405               };
48406             }
48407             
48408             var cfg = {
48409                 cls: 'form-group',
48410                 cn: []
48411             };
48412             
48413             var input =  {
48414                 tag: 'input',
48415                 id : id,
48416                 // type: 'number', -- do not use number - we get the flaky up/down arrows.
48417                 maxlength: this.max_length,
48418                 cls : 'form-control tel-input',
48419                 autocomplete: 'new-password'
48420             };
48421             
48422             var hiddenInput = {
48423                 tag: 'input',
48424                 type: 'hidden',
48425                 cls: 'hidden-tel-input'
48426             };
48427             
48428             if (this.name) {
48429                 hiddenInput.name = this.name;
48430             }
48431             
48432             if (this.disabled) {
48433                 input.disabled = true;
48434             }
48435             
48436             var flag_container = {
48437                 tag: 'div',
48438                 cls: 'flag-box',
48439                 cn: [
48440                     {
48441                         tag: 'div',
48442                         cls: 'flag'
48443                     },
48444                     {
48445                         tag: 'div',
48446                         cls: 'caret'
48447                     }
48448                 ]
48449             };
48450             
48451             var box = {
48452                 tag: 'div',
48453                 cls: this.hasFeedback ? 'has-feedback' : '',
48454                 cn: [
48455                     hiddenInput,
48456                     input,
48457                     {
48458                         tag: 'input',
48459                         cls: 'dial-code-holder',
48460                         disabled: true
48461                     }
48462                 ]
48463             };
48464             
48465             var container = {
48466                 cls: 'roo-select2-container input-group',
48467                 cn: [
48468                     flag_container,
48469                     box
48470                 ]
48471             };
48472             
48473             if (this.fieldLabel.length) {
48474                 var indicator = {
48475                     tag: 'i',
48476                     tooltip: 'This field is required'
48477                 };
48478                 
48479                 var label = {
48480                     tag: 'label',
48481                     'for':  id,
48482                     cls: 'control-label',
48483                     cn: []
48484                 };
48485                 
48486                 var label_text = {
48487                     tag: 'span',
48488                     html: this.fieldLabel
48489                 };
48490                 
48491                 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star left-indicator';
48492                 label.cn = [
48493                     indicator,
48494                     label_text
48495                 ];
48496                 
48497                 if(this.indicatorpos == 'right') {
48498                     indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star right-indicator';
48499                     label.cn = [
48500                         label_text,
48501                         indicator
48502                     ];
48503                 }
48504                 
48505                 if(align == 'left') {
48506                     container = {
48507                         tag: 'div',
48508                         cn: [
48509                             container
48510                         ]
48511                     };
48512                     
48513                     if(this.labelWidth > 12){
48514                         label.style = "width: " + this.labelWidth + 'px';
48515                     }
48516                     if(this.labelWidth < 13 && this.labelmd == 0){
48517                         this.labelmd = this.labelWidth;
48518                     }
48519                     if(this.labellg > 0){
48520                         label.cls += ' col-lg-' + this.labellg;
48521                         input.cls += ' col-lg-' + (12 - this.labellg);
48522                     }
48523                     if(this.labelmd > 0){
48524                         label.cls += ' col-md-' + this.labelmd;
48525                         container.cls += ' col-md-' + (12 - this.labelmd);
48526                     }
48527                     if(this.labelsm > 0){
48528                         label.cls += ' col-sm-' + this.labelsm;
48529                         container.cls += ' col-sm-' + (12 - this.labelsm);
48530                     }
48531                     if(this.labelxs > 0){
48532                         label.cls += ' col-xs-' + this.labelxs;
48533                         container.cls += ' col-xs-' + (12 - this.labelxs);
48534                     }
48535                 }
48536             }
48537             
48538             cfg.cn = [
48539                 label,
48540                 container
48541             ];
48542             
48543             var settings = this;
48544             
48545             ['xs','sm','md','lg'].map(function(size){
48546                 if (settings[size]) {
48547                     cfg.cls += ' col-' + size + '-' + settings[size];
48548                 }
48549             });
48550             
48551             this.store = new Roo.data.Store({
48552                 proxy : new Roo.data.MemoryProxy({}),
48553                 reader : new Roo.data.JsonReader({
48554                     fields : [
48555                         {
48556                             'name' : 'name',
48557                             'type' : 'string'
48558                         },
48559                         {
48560                             'name' : 'iso2',
48561                             'type' : 'string'
48562                         },
48563                         {
48564                             'name' : 'dialCode',
48565                             'type' : 'string'
48566                         },
48567                         {
48568                             'name' : 'priority',
48569                             'type' : 'string'
48570                         },
48571                         {
48572                             'name' : 'areaCodes',
48573                             'type' : 'string'
48574                         }
48575                     ]
48576                 })
48577             });
48578             
48579             if(!this.preferedCountries) {
48580                 this.preferedCountries = [
48581                     'hk',
48582                     'gb',
48583                     'us'
48584                 ];
48585             }
48586             
48587             var p = this.preferedCountries.reverse();
48588             
48589             if(p) {
48590                 for (var i = 0; i < p.length; i++) {
48591                     for (var j = 0; j < this.allCountries.length; j++) {
48592                         if(this.allCountries[j].iso2 == p[i]) {
48593                             var t = this.allCountries[j];
48594                             this.allCountries.splice(j,1);
48595                             this.allCountries.unshift(t);
48596                         }
48597                     } 
48598                 }
48599             }
48600             
48601             this.store.proxy.data = {
48602                 success: true,
48603                 data: this.allCountries
48604             };
48605             
48606             return cfg;
48607         },
48608         
48609         initEvents : function()
48610         {
48611             this.createList();
48612             Roo.bootstrap.form.PhoneInput.superclass.initEvents.call(this);
48613             
48614             this.indicator = this.indicatorEl();
48615             this.flag = this.flagEl();
48616             this.dialCodeHolder = this.dialCodeHolderEl();
48617             
48618             this.trigger = this.el.select('div.flag-box',true).first();
48619             this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
48620             
48621             var _this = this;
48622             
48623             (function(){
48624                 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
48625                 _this.list.setWidth(lw);
48626             }).defer(100);
48627             
48628             this.list.on('mouseover', this.onViewOver, this);
48629             this.list.on('mousemove', this.onViewMove, this);
48630             this.inputEl().on("keyup", this.onKeyUp, this);
48631             this.inputEl().on("keypress", this.onKeyPress, this);
48632             
48633             this.tpl = '<li><a href="#"><div class="flag {iso2}"></div>{name} <span class="dial-code">+{dialCode}</span></a></li>';
48634
48635             this.view = new Roo.View(this.list, this.tpl, {
48636                 singleSelect:true, store: this.store, selectedClass: this.selectedClass
48637             });
48638             
48639             this.view.on('click', this.onViewClick, this);
48640             this.setValue(this.defaultDialCode);
48641         },
48642         
48643         onTriggerClick : function(e)
48644         {
48645             Roo.log('trigger click');
48646             if(this.disabled){
48647                 return;
48648             }
48649             
48650             if(this.isExpanded()){
48651                 this.collapse();
48652                 this.hasFocus = false;
48653             }else {
48654                 this.store.load({});
48655                 this.hasFocus = true;
48656                 this.expand();
48657             }
48658         },
48659         
48660         isExpanded : function()
48661         {
48662             return this.list.isVisible();
48663         },
48664         
48665         collapse : function()
48666         {
48667             if(!this.isExpanded()){
48668                 return;
48669             }
48670             this.list.hide();
48671             Roo.get(document).un('mousedown', this.collapseIf, this);
48672             Roo.get(document).un('mousewheel', this.collapseIf, this);
48673             this.fireEvent('collapse', this);
48674             this.validate();
48675         },
48676         
48677         expand : function()
48678         {
48679             Roo.log('expand');
48680
48681             if(this.isExpanded() || !this.hasFocus){
48682                 return;
48683             }
48684             
48685             var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
48686             this.list.setWidth(lw);
48687             
48688             this.list.show();
48689             this.restrictHeight();
48690             
48691             Roo.get(document).on('mousedown', this.collapseIf, this);
48692             Roo.get(document).on('mousewheel', this.collapseIf, this);
48693             
48694             this.fireEvent('expand', this);
48695         },
48696         
48697         restrictHeight : function()
48698         {
48699             this.list.alignTo(this.inputEl(), this.listAlign);
48700             this.list.alignTo(this.inputEl(), this.listAlign);
48701         },
48702         
48703         onViewOver : function(e, t)
48704         {
48705             if(this.inKeyMode){
48706                 return;
48707             }
48708             var item = this.view.findItemFromChild(t);
48709             
48710             if(item){
48711                 var index = this.view.indexOf(item);
48712                 this.select(index, false);
48713             }
48714         },
48715
48716         // private
48717         onViewClick : function(view, doFocus, el, e)
48718         {
48719             var index = this.view.getSelectedIndexes()[0];
48720             
48721             var r = this.store.getAt(index);
48722             
48723             if(r){
48724                 this.onSelect(r, index);
48725             }
48726             if(doFocus !== false && !this.blockFocus){
48727                 this.inputEl().focus();
48728             }
48729         },
48730         
48731         onViewMove : function(e, t)
48732         {
48733             this.inKeyMode = false;
48734         },
48735         
48736         select : function(index, scrollIntoView)
48737         {
48738             this.selectedIndex = index;
48739             this.view.select(index);
48740             if(scrollIntoView !== false){
48741                 var el = this.view.getNode(index);
48742                 if(el){
48743                     this.list.scrollChildIntoView(el, false);
48744                 }
48745             }
48746         },
48747         
48748         createList : function()
48749         {
48750             this.list = Roo.get(document.body).createChild({
48751                 tag: 'ul',
48752                 cls: 'typeahead typeahead-long dropdown-menu tel-list',
48753                 style: 'display:none'
48754             });
48755             
48756             this.list.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
48757         },
48758         
48759         collapseIf : function(e)
48760         {
48761             var in_combo  = e.within(this.el);
48762             var in_list =  e.within(this.list);
48763             var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
48764             
48765             if (in_combo || in_list || is_list) {
48766                 return;
48767             }
48768             this.collapse();
48769         },
48770         
48771         onSelect : function(record, index)
48772         {
48773             if(this.fireEvent('beforeselect', this, record, index) !== false){
48774                 
48775                 this.setFlagClass(record.data.iso2);
48776                 this.setDialCode(record.data.dialCode);
48777                 this.hasFocus = false;
48778                 this.collapse();
48779                 this.fireEvent('select', this, record, index);
48780             }
48781         },
48782         
48783         flagEl : function()
48784         {
48785             var flag = this.el.select('div.flag',true).first();
48786             if(!flag){
48787                 return false;
48788             }
48789             return flag;
48790         },
48791         
48792         dialCodeHolderEl : function()
48793         {
48794             var d = this.el.select('input.dial-code-holder',true).first();
48795             if(!d){
48796                 return false;
48797             }
48798             return d;
48799         },
48800         
48801         setDialCode : function(v)
48802         {
48803             this.dialCodeHolder.dom.value = '+'+v;
48804         },
48805         
48806         setFlagClass : function(n)
48807         {
48808             this.flag.dom.className = 'flag '+n;
48809         },
48810         
48811         getValue : function()
48812         {
48813             var v = this.inputEl().getValue();
48814             if(this.dialCodeHolder) {
48815                 v = this.dialCodeHolder.dom.value+this.inputEl().getValue();
48816             }
48817             return v;
48818         },
48819         
48820         setValue : function(v)
48821         {
48822             var d = this.getDialCode(v);
48823             
48824             //invalid dial code
48825             if(v.length == 0 || !d || d.length == 0) {
48826                 if(this.rendered){
48827                     this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
48828                     this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
48829                 }
48830                 return;
48831             }
48832             
48833             //valid dial code
48834             this.setFlagClass(this.dialCodeMapping[d].iso2);
48835             this.setDialCode(d);
48836             this.inputEl().dom.value = v.replace('+'+d,'');
48837             this.hiddenEl().dom.value = this.getValue();
48838             
48839             this.validate();
48840         },
48841         
48842         getDialCode : function(v)
48843         {
48844             v = v ||  '';
48845             
48846             if (v.length == 0) {
48847                 return this.dialCodeHolder.dom.value;
48848             }
48849             
48850             var dialCode = "";
48851             if (v.charAt(0) != "+") {
48852                 return false;
48853             }
48854             var numericChars = "";
48855             for (var i = 1; i < v.length; i++) {
48856               var c = v.charAt(i);
48857               if (!isNaN(c)) {
48858                 numericChars += c;
48859                 if (this.dialCodeMapping[numericChars]) {
48860                   dialCode = v.substr(1, i);
48861                 }
48862                 if (numericChars.length == 4) {
48863                   break;
48864                 }
48865               }
48866             }
48867             return dialCode;
48868         },
48869         
48870         reset : function()
48871         {
48872             this.setValue(this.defaultDialCode);
48873             this.validate();
48874         },
48875         
48876         hiddenEl : function()
48877         {
48878             return this.el.select('input.hidden-tel-input',true).first();
48879         },
48880         
48881         // after setting val
48882         onKeyUp : function(e){
48883             this.setValue(this.getValue());
48884         },
48885         
48886         onKeyPress : function(e){
48887             if(this.allowed.indexOf(String.fromCharCode(e.getCharCode())) === -1){
48888                 e.stopEvent();
48889             }
48890         }
48891         
48892 });
48893 /**
48894  * @class Roo.bootstrap.form.MoneyField
48895  * @extends Roo.bootstrap.form.ComboBox
48896  * Bootstrap MoneyField class
48897  * 
48898  * @constructor
48899  * Create a new MoneyField.
48900  * @param {Object} config Configuration options
48901  */
48902
48903 Roo.bootstrap.form.MoneyField = function(config) {
48904     
48905     Roo.bootstrap.form.MoneyField.superclass.constructor.call(this, config);
48906     
48907 };
48908
48909 Roo.extend(Roo.bootstrap.form.MoneyField, Roo.bootstrap.form.ComboBox, {
48910     
48911     /**
48912      * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
48913      */
48914     allowDecimals : true,
48915     /**
48916      * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
48917      */
48918     decimalSeparator : ".",
48919     /**
48920      * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
48921      */
48922     decimalPrecision : 0,
48923     /**
48924      * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
48925      */
48926     allowNegative : true,
48927     /**
48928      * @cfg {Boolean} allowZero False to blank out if the user enters '0' (defaults to true)
48929      */
48930     allowZero: true,
48931     /**
48932      * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
48933      */
48934     minValue : Number.NEGATIVE_INFINITY,
48935     /**
48936      * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
48937      */
48938     maxValue : Number.MAX_VALUE,
48939     /**
48940      * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
48941      */
48942     minText : "The minimum value for this field is {0}",
48943     /**
48944      * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
48945      */
48946     maxText : "The maximum value for this field is {0}",
48947     /**
48948      * @cfg {String} nanText Error text to display if the value is not a valid number.  For example, this can happen
48949      * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
48950      */
48951     nanText : "{0} is not a valid number",
48952     /**
48953      * @cfg {Boolean} castInt (true|false) cast int if true (defalut true)
48954      */
48955     castInt : true,
48956     /**
48957      * @cfg {String} defaults currency of the MoneyField
48958      * value should be in lkey
48959      */
48960     defaultCurrency : false,
48961     /**
48962      * @cfg {String} thousandsDelimiter Symbol of thousandsDelimiter
48963      */
48964     thousandsDelimiter : false,
48965     /**
48966      * @cfg {Number} max_length Maximum input field length allowed (defaults to Number.MAX_VALUE)
48967      */
48968     max_length: false,
48969     
48970     inputlg : 9,
48971     inputmd : 9,
48972     inputsm : 9,
48973     inputxs : 6,
48974      /**
48975      * @cfg {Roo.data.Store} store  Store to lookup currency??
48976      */
48977     store : false,
48978     
48979     getAutoCreate : function()
48980     {
48981         var align = this.labelAlign || this.parentLabelAlign();
48982         
48983         var id = Roo.id();
48984
48985         var cfg = {
48986             cls: 'form-group',
48987             cn: []
48988         };
48989
48990         var input =  {
48991             tag: 'input',
48992             id : id,
48993             cls : 'form-control roo-money-amount-input',
48994             autocomplete: 'new-password'
48995         };
48996         
48997         var hiddenInput = {
48998             tag: 'input',
48999             type: 'hidden',
49000             id: Roo.id(),
49001             cls: 'hidden-number-input'
49002         };
49003         
49004         if(this.max_length) {
49005             input.maxlength = this.max_length; 
49006         }
49007         
49008         if (this.name) {
49009             hiddenInput.name = this.name;
49010         }
49011
49012         if (this.disabled) {
49013             input.disabled = true;
49014         }
49015
49016         var clg = 12 - this.inputlg;
49017         var cmd = 12 - this.inputmd;
49018         var csm = 12 - this.inputsm;
49019         var cxs = 12 - this.inputxs;
49020         
49021         var container = {
49022             tag : 'div',
49023             cls : 'row roo-money-field',
49024             cn : [
49025                 {
49026                     tag : 'div',
49027                     cls : 'roo-money-currency column col-lg-' + clg + ' col-md-' + cmd + ' col-sm-' + csm + ' col-xs-' + cxs,
49028                     cn : [
49029                         {
49030                             tag : 'div',
49031                             cls: 'roo-select2-container input-group',
49032                             cn: [
49033                                 {
49034                                     tag : 'input',
49035                                     cls : 'form-control roo-money-currency-input',
49036                                     autocomplete: 'new-password',
49037                                     readOnly : 1,
49038                                     name : this.currencyName
49039                                 },
49040                                 {
49041                                     tag :'span',
49042                                     cls : 'input-group-addon',
49043                                     cn : [
49044                                         {
49045                                             tag: 'span',
49046                                             cls: 'caret'
49047                                         }
49048                                     ]
49049                                 }
49050                             ]
49051                         }
49052                     ]
49053                 },
49054                 {
49055                     tag : 'div',
49056                     cls : 'roo-money-amount column col-lg-' + this.inputlg + ' col-md-' + this.inputmd + ' col-sm-' + this.inputsm + ' col-xs-' + this.inputxs,
49057                     cn : [
49058                         {
49059                             tag: 'div',
49060                             cls: this.hasFeedback ? 'has-feedback' : '',
49061                             cn: [
49062                                 input
49063                             ]
49064                         }
49065                     ]
49066                 }
49067             ]
49068             
49069         };
49070         
49071         if (this.fieldLabel.length) {
49072             var indicator = {
49073                 tag: 'i',
49074                 tooltip: 'This field is required'
49075             };
49076
49077             var label = {
49078                 tag: 'label',
49079                 'for':  id,
49080                 cls: 'control-label',
49081                 cn: []
49082             };
49083
49084             var label_text = {
49085                 tag: 'span',
49086                 html: this.fieldLabel
49087             };
49088
49089             indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star left-indicator';
49090             label.cn = [
49091                 indicator,
49092                 label_text
49093             ];
49094
49095             if(this.indicatorpos == 'right') {
49096                 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star right-indicator';
49097                 label.cn = [
49098                     label_text,
49099                     indicator
49100                 ];
49101             }
49102
49103             if(align == 'left') {
49104                 container = {
49105                     tag: 'div',
49106                     cn: [
49107                         container
49108                     ]
49109                 };
49110
49111                 if(this.labelWidth > 12){
49112                     label.style = "width: " + this.labelWidth + 'px';
49113                 }
49114                 if(this.labelWidth < 13 && this.labelmd == 0){
49115                     this.labelmd = this.labelWidth;
49116                 }
49117                 if(this.labellg > 0){
49118                     label.cls += ' col-lg-' + this.labellg;
49119                     input.cls += ' col-lg-' + (12 - this.labellg);
49120                 }
49121                 if(this.labelmd > 0){
49122                     label.cls += ' col-md-' + this.labelmd;
49123                     container.cls += ' col-md-' + (12 - this.labelmd);
49124                 }
49125                 if(this.labelsm > 0){
49126                     label.cls += ' col-sm-' + this.labelsm;
49127                     container.cls += ' col-sm-' + (12 - this.labelsm);
49128                 }
49129                 if(this.labelxs > 0){
49130                     label.cls += ' col-xs-' + this.labelxs;
49131                     container.cls += ' col-xs-' + (12 - this.labelxs);
49132                 }
49133             }
49134         }
49135
49136         cfg.cn = [
49137             label,
49138             container,
49139             hiddenInput
49140         ];
49141         
49142         var settings = this;
49143
49144         ['xs','sm','md','lg'].map(function(size){
49145             if (settings[size]) {
49146                 cfg.cls += ' col-' + size + '-' + settings[size];
49147             }
49148         });
49149         
49150         return cfg;
49151     },
49152     
49153     initEvents : function()
49154     {
49155         this.indicator = this.indicatorEl();
49156         
49157         this.initCurrencyEvent();
49158         
49159         this.initNumberEvent();
49160     },
49161     
49162     initCurrencyEvent : function()
49163     {
49164         if (!this.store) {
49165             throw "can not find store for combo";
49166         }
49167         
49168         this.store = Roo.factory(this.store, Roo.data);
49169         this.store.parent = this;
49170         
49171         this.createList();
49172         
49173         this.triggerEl = this.el.select('.input-group-addon', true).first();
49174         
49175         this.triggerEl.on("click", this.onTriggerClick, this, { preventDefault : true });
49176         
49177         var _this = this;
49178         
49179         (function(){
49180             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
49181             _this.list.setWidth(lw);
49182         }).defer(100);
49183         
49184         this.list.on('mouseover', this.onViewOver, this);
49185         this.list.on('mousemove', this.onViewMove, this);
49186         this.list.on('scroll', this.onViewScroll, this);
49187         
49188         if(!this.tpl){
49189             this.tpl = '<li><a href="#">{' + this.currencyField + '}</a></li>';
49190         }
49191         
49192         this.view = new Roo.View(this.list, this.tpl, {
49193             singleSelect:true, store: this.store, selectedClass: this.selectedClass
49194         });
49195         
49196         this.view.on('click', this.onViewClick, this);
49197         
49198         this.store.on('beforeload', this.onBeforeLoad, this);
49199         this.store.on('load', this.onLoad, this);
49200         this.store.on('loadexception', this.onLoadException, this);
49201         
49202         this.keyNav = new Roo.KeyNav(this.currencyEl(), {
49203             "up" : function(e){
49204                 this.inKeyMode = true;
49205                 this.selectPrev();
49206             },
49207
49208             "down" : function(e){
49209                 if(!this.isExpanded()){
49210                     this.onTriggerClick();
49211                 }else{
49212                     this.inKeyMode = true;
49213                     this.selectNext();
49214                 }
49215             },
49216
49217             "enter" : function(e){
49218                 this.collapse();
49219                 
49220                 if(this.fireEvent("specialkey", this, e)){
49221                     this.onViewClick(false);
49222                 }
49223                 
49224                 return true;
49225             },
49226
49227             "esc" : function(e){
49228                 this.collapse();
49229             },
49230
49231             "tab" : function(e){
49232                 this.collapse();
49233                 
49234                 if(this.fireEvent("specialkey", this, e)){
49235                     this.onViewClick(false);
49236                 }
49237                 
49238                 return true;
49239             },
49240
49241             scope : this,
49242
49243             doRelay : function(foo, bar, hname){
49244                 if(hname == 'down' || this.scope.isExpanded()){
49245                    return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
49246                 }
49247                 return true;
49248             },
49249
49250             forceKeyDown: true
49251         });
49252         
49253         this.currencyEl().on("click", this.onTriggerClick, this, { preventDefault : true });
49254         
49255     },
49256     
49257     initNumberEvent : function(e)
49258     {
49259         this.inputEl().on("keydown" , this.fireKey,  this);
49260         this.inputEl().on("focus", this.onFocus,  this);
49261         this.inputEl().on("blur", this.onBlur,  this);
49262         
49263         this.inputEl().relayEvent('keyup', this);
49264         
49265         if(this.indicator){
49266             this.indicator.addClass('invisible');
49267         }
49268  
49269         this.originalValue = this.getValue();
49270         
49271         if(this.validationEvent == 'keyup'){
49272             this.validationTask = new Roo.util.DelayedTask(this.validate, this);
49273             this.inputEl().on('keyup', this.filterValidation, this);
49274         }
49275         else if(this.validationEvent !== false){
49276             this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
49277         }
49278         
49279         if(this.selectOnFocus){
49280             this.on("focus", this.preFocus, this);
49281             
49282         }
49283         if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
49284             this.inputEl().on("keypress", this.filterKeys, this);
49285         } else {
49286             this.inputEl().relayEvent('keypress', this);
49287         }
49288         
49289         var allowed = "0123456789";
49290         
49291         if(this.allowDecimals){
49292             allowed += this.decimalSeparator;
49293         }
49294         
49295         if(this.allowNegative){
49296             allowed += "-";
49297         }
49298         
49299         if(this.thousandsDelimiter) {
49300             allowed += ",";
49301         }
49302         
49303         this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
49304         
49305         var keyPress = function(e){
49306             
49307             var k = e.getKey();
49308             
49309             var c = e.getCharCode();
49310             
49311             if(
49312                     (String.fromCharCode(c) == '.' || String.fromCharCode(c) == '-') &&
49313                     allowed.indexOf(String.fromCharCode(c)) === -1
49314             ){
49315                 e.stopEvent();
49316                 return;
49317             }
49318             
49319             if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
49320                 return;
49321             }
49322             
49323             if(allowed.indexOf(String.fromCharCode(c)) === -1){
49324                 e.stopEvent();
49325             }
49326         };
49327         
49328         this.inputEl().on("keypress", keyPress, this);
49329         
49330     },
49331     
49332     onTriggerClick : function(e)
49333     {   
49334         if(this.disabled){
49335             return;
49336         }
49337         
49338         this.page = 0;
49339         this.loadNext = false;
49340         
49341         if(this.isExpanded()){
49342             this.collapse();
49343             return;
49344         }
49345         
49346         this.hasFocus = true;
49347         
49348         if(this.triggerAction == 'all') {
49349             this.doQuery(this.allQuery, true);
49350             return;
49351         }
49352         
49353         this.doQuery(this.getRawValue());
49354     },
49355     
49356     getCurrency : function()
49357     {   
49358         var v = this.currencyEl().getValue();
49359         
49360         return v;
49361     },
49362     
49363     restrictHeight : function()
49364     {
49365         this.list.alignTo(this.currencyEl(), this.listAlign);
49366         this.list.alignTo(this.currencyEl(), this.listAlign);
49367     },
49368     
49369     onViewClick : function(view, doFocus, el, e)
49370     {
49371         var index = this.view.getSelectedIndexes()[0];
49372         
49373         var r = this.store.getAt(index);
49374         
49375         if(r){
49376             this.onSelect(r, index);
49377         }
49378     },
49379     
49380     onSelect : function(record, index){
49381         
49382         if(this.fireEvent('beforeselect', this, record, index) !== false){
49383         
49384             this.setFromCurrencyData(index > -1 ? record.data : false);
49385             
49386             this.collapse();
49387             
49388             this.fireEvent('select', this, record, index);
49389         }
49390     },
49391     
49392     setFromCurrencyData : function(o)
49393     {
49394         var currency = '';
49395         
49396         this.lastCurrency = o;
49397         
49398         if (this.currencyField) {
49399             currency = !o || typeof(o[this.currencyField]) == 'undefined' ? '' : o[this.currencyField];
49400         } else {
49401             Roo.log('no  currencyField value set for '+ (this.name ? this.name : this.id));
49402         }
49403         
49404         this.lastSelectionText = currency;
49405         
49406         //setting default currency
49407         if(o[this.currencyField] * 1 == 0 && this.defaultCurrency) {
49408             this.setCurrency(this.defaultCurrency);
49409             return;
49410         }
49411         
49412         this.setCurrency(currency);
49413     },
49414     
49415     setFromData : function(o)
49416     {
49417         var c = {};
49418         
49419         c[this.currencyField] = !o || typeof(o[this.currencyName]) == 'undefined' ? '' : o[this.currencyName];
49420         
49421         this.setFromCurrencyData(c);
49422         
49423         var value = '';
49424         
49425         if (this.name) {
49426             value = !o || typeof(o[this.name]) == 'undefined' ? '' : o[this.name];
49427         } else {
49428             Roo.log('no value set for '+ (this.name ? this.name : this.id));
49429         }
49430         
49431         this.setValue(value);
49432         
49433     },
49434     
49435     setCurrency : function(v)
49436     {   
49437         this.currencyValue = v;
49438         
49439         if(this.rendered){
49440             this.currencyEl().dom.value = (v === null || v === undefined ? '' : v);
49441             this.validate();
49442         }
49443     },
49444     
49445     setValue : function(v)
49446     {
49447         v = String(this.fixPrecision(v)).replace(".", this.decimalSeparator);
49448         
49449         this.value = v;
49450         
49451         if(this.rendered){
49452             
49453             this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
49454             
49455             this.inputEl().dom.value = (v == '') ? '' :
49456                 Roo.util.Format.number(v, this.decimalPrecision, this.thousandsDelimiter || '');
49457             
49458             if(!this.allowZero && v === '0') {
49459                 this.hiddenEl().dom.value = '';
49460                 this.inputEl().dom.value = '';
49461             }
49462             
49463             this.validate();
49464         }
49465     },
49466     
49467     getRawValue : function()
49468     {
49469         var v = this.inputEl().getValue();
49470         
49471         return v;
49472     },
49473     
49474     getValue : function()
49475     {
49476         return this.fixPrecision(this.parseValue(this.getRawValue()));
49477     },
49478     
49479     parseValue : function(value)
49480     {
49481         if(this.thousandsDelimiter) {
49482             value += "";
49483             r = new RegExp(",", "g");
49484             value = value.replace(r, "");
49485         }
49486         
49487         value = parseFloat(String(value).replace(this.decimalSeparator, "."));
49488         return isNaN(value) ? '' : value;
49489         
49490     },
49491     
49492     fixPrecision : function(value)
49493     {
49494         if(this.thousandsDelimiter) {
49495             value += "";
49496             r = new RegExp(",", "g");
49497             value = value.replace(r, "");
49498         }
49499         
49500         var nan = isNaN(value);
49501         
49502         if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
49503             return nan ? '' : value;
49504         }
49505         return parseFloat(value).toFixed(this.decimalPrecision);
49506     },
49507     
49508     decimalPrecisionFcn : function(v)
49509     {
49510         return Math.floor(v);
49511     },
49512     
49513     validateValue : function(value)
49514     {
49515         if(!Roo.bootstrap.form.MoneyField.superclass.validateValue.call(this, value)){
49516             return false;
49517         }
49518         
49519         var num = this.parseValue(value);
49520         
49521         if(isNaN(num)){
49522             this.markInvalid(String.format(this.nanText, value));
49523             return false;
49524         }
49525         
49526         if(num < this.minValue){
49527             this.markInvalid(String.format(this.minText, this.minValue));
49528             return false;
49529         }
49530         
49531         if(num > this.maxValue){
49532             this.markInvalid(String.format(this.maxText, this.maxValue));
49533             return false;
49534         }
49535         
49536         return true;
49537     },
49538     
49539     validate : function()
49540     {
49541         if(this.disabled || this.allowBlank){
49542             this.markValid();
49543             return true;
49544         }
49545         
49546         var currency = this.getCurrency();
49547         
49548         if(this.validateValue(this.getRawValue()) && currency.length){
49549             this.markValid();
49550             return true;
49551         }
49552         
49553         this.markInvalid();
49554         return false;
49555     },
49556     
49557     getName: function()
49558     {
49559         return this.name;
49560     },
49561     
49562     beforeBlur : function()
49563     {
49564         if(!this.castInt){
49565             return;
49566         }
49567         
49568         var v = this.parseValue(this.getRawValue());
49569         
49570         if(v || v == 0){
49571             this.setValue(v);
49572         }
49573     },
49574     
49575     onBlur : function()
49576     {
49577         this.beforeBlur();
49578         
49579         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
49580             //this.el.removeClass(this.focusClass);
49581         }
49582         
49583         this.hasFocus = false;
49584         
49585         if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
49586             this.validate();
49587         }
49588         
49589         var v = this.getValue();
49590         
49591         if(String(v) !== String(this.startValue)){
49592             this.fireEvent('change', this, v, this.startValue);
49593         }
49594         
49595         this.fireEvent("blur", this);
49596     },
49597     
49598     inputEl : function()
49599     {
49600         return this.el.select('.roo-money-amount-input', true).first();
49601     },
49602     
49603     currencyEl : function()
49604     {
49605         return this.el.select('.roo-money-currency-input', true).first();
49606     },
49607     
49608     hiddenEl : function()
49609     {
49610         return this.el.select('input.hidden-number-input',true).first();
49611     }
49612     
49613 });/**
49614  * @class Roo.bootstrap.BezierSignature
49615  * @extends Roo.bootstrap.Component
49616  * Bootstrap BezierSignature class
49617  * This script refer to:
49618  *    Title: Signature Pad
49619  *    Author: szimek
49620  *    Availability: https://github.com/szimek/signature_pad
49621  *
49622  * @constructor
49623  * Create a new BezierSignature
49624  * @param {Object} config The config object
49625  */
49626
49627 Roo.bootstrap.BezierSignature = function(config){
49628     Roo.bootstrap.BezierSignature.superclass.constructor.call(this, config);
49629     this.addEvents({
49630         "resize" : true
49631     });
49632 };
49633
49634 Roo.extend(Roo.bootstrap.BezierSignature, Roo.bootstrap.Component,
49635 {
49636      
49637     curve_data: [],
49638     
49639     is_empty: true,
49640     
49641     mouse_btn_down: true,
49642     
49643     /**
49644      * @cfg {int} canvas height
49645      */
49646     canvas_height: '200px',
49647     
49648     /**
49649      * @cfg {float|function} Radius of a single dot.
49650      */ 
49651     dot_size: false,
49652     
49653     /**
49654      * @cfg {float} Minimum width of a line. Defaults to 0.5.
49655      */
49656     min_width: 0.5,
49657     
49658     /**
49659      * @cfg {float} Maximum width of a line. Defaults to 2.5.
49660      */
49661     max_width: 2.5,
49662     
49663     /**
49664      * @cfg {integer} Draw the next point at most once per every x milliseconds. Set it to 0 to turn off throttling. Defaults to 16.
49665      */
49666     throttle: 16,
49667     
49668     /**
49669      * @cfg {integer} Add the next point only if the previous one is farther than x pixels. Defaults to 5.
49670      */
49671     min_distance: 5,
49672     
49673     /**
49674      * @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.
49675      */
49676     bg_color: 'rgba(0, 0, 0, 0)',
49677     
49678     /**
49679      * @cfg {string} Color used to draw the lines. Can be any color format accepted by context.fillStyle. Defaults to "black".
49680      */
49681     dot_color: 'black',
49682     
49683     /**
49684      * @cfg {float} Weight used to modify new velocity based on the previous velocity. Defaults to 0.7.
49685      */ 
49686     velocity_filter_weight: 0.7,
49687     
49688     /**
49689      * @cfg {function} Callback when stroke begin. 
49690      */
49691     onBegin: false,
49692     
49693     /**
49694      * @cfg {function} Callback when stroke end.
49695      */
49696     onEnd: false,
49697     
49698     getAutoCreate : function()
49699     {
49700         var cls = 'roo-signature column';
49701         
49702         if(this.cls){
49703             cls += ' ' + this.cls;
49704         }
49705         
49706         var col_sizes = [
49707             'lg',
49708             'md',
49709             'sm',
49710             'xs'
49711         ];
49712         
49713         for(var i = 0; i < col_sizes.length; i++) {
49714             if(this[col_sizes[i]]) {
49715                 cls += " col-"+col_sizes[i]+"-"+this[col_sizes[i]];
49716             }
49717         }
49718         
49719         var cfg = {
49720             tag: 'div',
49721             cls: cls,
49722             cn: [
49723                 {
49724                     tag: 'div',
49725                     cls: 'roo-signature-body',
49726                     cn: [
49727                         {
49728                             tag: 'canvas',
49729                             cls: 'roo-signature-body-canvas',
49730                             height: this.canvas_height,
49731                             width: this.canvas_width
49732                         }
49733                     ]
49734                 },
49735                 {
49736                     tag: 'input',
49737                     type: 'file',
49738                     style: 'display: none'
49739                 }
49740             ]
49741         };
49742         
49743         return cfg;
49744     },
49745     
49746     initEvents: function() 
49747     {
49748         Roo.bootstrap.BezierSignature.superclass.initEvents.call(this);
49749         
49750         var canvas = this.canvasEl();
49751         
49752         // mouse && touch event swapping...
49753         canvas.dom.style.touchAction = 'none';
49754         canvas.dom.style.msTouchAction = 'none';
49755         
49756         this.mouse_btn_down = false;
49757         canvas.on('mousedown', this._handleMouseDown, this);
49758         canvas.on('mousemove', this._handleMouseMove, this);
49759         Roo.select('html').first().on('mouseup', this._handleMouseUp, this);
49760         
49761         if (window.PointerEvent) {
49762             canvas.on('pointerdown', this._handleMouseDown, this);
49763             canvas.on('pointermove', this._handleMouseMove, this);
49764             Roo.select('html').first().on('pointerup', this._handleMouseUp, this);
49765         }
49766         
49767         if ('ontouchstart' in window) {
49768             canvas.on('touchstart', this._handleTouchStart, this);
49769             canvas.on('touchmove', this._handleTouchMove, this);
49770             canvas.on('touchend', this._handleTouchEnd, this);
49771         }
49772         
49773         Roo.EventManager.onWindowResize(this.resize, this, true);
49774         
49775         // file input event
49776         this.fileEl().on('change', this.uploadImage, this);
49777         
49778         this.clear();
49779         
49780         this.resize();
49781     },
49782     
49783     resize: function(){
49784         
49785         var canvas = this.canvasEl().dom;
49786         var ctx = this.canvasElCtx();
49787         var img_data = false;
49788         
49789         if(canvas.width > 0) {
49790             var img_data = ctx.getImageData(0, 0, canvas.width, canvas.height);
49791         }
49792         // setting canvas width will clean img data
49793         canvas.width = 0;
49794         
49795         var style = window.getComputedStyle ? 
49796             getComputedStyle(this.el.dom, null) : this.el.dom.currentStyle;
49797             
49798         var padding_left = parseInt(style.paddingLeft) || 0;
49799         var padding_right = parseInt(style.paddingRight) || 0;
49800         
49801         canvas.width = this.el.dom.clientWidth - padding_left - padding_right;
49802         
49803         if(img_data) {
49804             ctx.putImageData(img_data, 0, 0);
49805         }
49806     },
49807     
49808     _handleMouseDown: function(e)
49809     {
49810         if (e.browserEvent.which === 1) {
49811             this.mouse_btn_down = true;
49812             this.strokeBegin(e);
49813         }
49814     },
49815     
49816     _handleMouseMove: function (e)
49817     {
49818         if (this.mouse_btn_down) {
49819             this.strokeMoveUpdate(e);
49820         }
49821     },
49822     
49823     _handleMouseUp: function (e)
49824     {
49825         if (e.browserEvent.which === 1 && this.mouse_btn_down) {
49826             this.mouse_btn_down = false;
49827             this.strokeEnd(e);
49828         }
49829     },
49830     
49831     _handleTouchStart: function (e) {
49832         
49833         e.preventDefault();
49834         if (e.browserEvent.targetTouches.length === 1) {
49835             // var touch = e.browserEvent.changedTouches[0];
49836             // this.strokeBegin(touch);
49837             
49838              this.strokeBegin(e); // assume e catching the correct xy...
49839         }
49840     },
49841     
49842     _handleTouchMove: function (e) {
49843         e.preventDefault();
49844         // var touch = event.targetTouches[0];
49845         // _this._strokeMoveUpdate(touch);
49846         this.strokeMoveUpdate(e);
49847     },
49848     
49849     _handleTouchEnd: function (e) {
49850         var wasCanvasTouched = e.target === this.canvasEl().dom;
49851         if (wasCanvasTouched) {
49852             e.preventDefault();
49853             // var touch = event.changedTouches[0];
49854             // _this._strokeEnd(touch);
49855             this.strokeEnd(e);
49856         }
49857     },
49858     
49859     reset: function () {
49860         this._lastPoints = [];
49861         this._lastVelocity = 0;
49862         this._lastWidth = (this.min_width + this.max_width) / 2;
49863         this.canvasElCtx().fillStyle = this.dot_color;
49864     },
49865     
49866     strokeMoveUpdate: function(e)
49867     {
49868         this.strokeUpdate(e);
49869         
49870         if (this.throttle) {
49871             this.throttleStroke(this.strokeUpdate, this.throttle);
49872         }
49873         else {
49874             this.strokeUpdate(e);
49875         }
49876     },
49877     
49878     strokeBegin: function(e)
49879     {
49880         var newPointGroup = {
49881             color: this.dot_color,
49882             points: []
49883         };
49884         
49885         if (typeof this.onBegin === 'function') {
49886             this.onBegin(e);
49887         }
49888         
49889         this.curve_data.push(newPointGroup);
49890         this.reset();
49891         this.strokeUpdate(e);
49892     },
49893     
49894     strokeUpdate: function(e)
49895     {
49896         var rect = this.canvasEl().dom.getBoundingClientRect();
49897         var point = new this.Point(e.xy[0] - rect.left, e.xy[1] - rect.top, new Date().getTime());
49898         var lastPointGroup = this.curve_data[this.curve_data.length - 1];
49899         var lastPoints = lastPointGroup.points;
49900         var lastPoint = lastPoints.length > 0 && lastPoints[lastPoints.length - 1];
49901         var isLastPointTooClose = lastPoint
49902             ? point.distanceTo(lastPoint) <= this.min_distance
49903             : false;
49904         var color = lastPointGroup.color;
49905         if (!lastPoint || !(lastPoint && isLastPointTooClose)) {
49906             var curve = this.addPoint(point);
49907             if (!lastPoint) {
49908                 this.drawDot({color: color, point: point});
49909             }
49910             else if (curve) {
49911                 this.drawCurve({color: color, curve: curve});
49912             }
49913             lastPoints.push({
49914                 time: point.time,
49915                 x: point.x,
49916                 y: point.y
49917             });
49918         }
49919     },
49920     
49921     strokeEnd: function(e)
49922     {
49923         this.strokeUpdate(e);
49924         if (typeof this.onEnd === 'function') {
49925             this.onEnd(e);
49926         }
49927     },
49928     
49929     addPoint:  function (point) {
49930         var _lastPoints = this._lastPoints;
49931         _lastPoints.push(point);
49932         if (_lastPoints.length > 2) {
49933             if (_lastPoints.length === 3) {
49934                 _lastPoints.unshift(_lastPoints[0]);
49935             }
49936             var widths = this.calculateCurveWidths(_lastPoints[1], _lastPoints[2]);
49937             var curve = this.Bezier.fromPoints(_lastPoints, widths, this);
49938             _lastPoints.shift();
49939             return curve;
49940         }
49941         return null;
49942     },
49943     
49944     calculateCurveWidths: function (startPoint, endPoint) {
49945         var velocity = this.velocity_filter_weight * endPoint.velocityFrom(startPoint) +
49946             (1 - this.velocity_filter_weight) * this._lastVelocity;
49947
49948         var newWidth = Math.max(this.max_width / (velocity + 1), this.min_width);
49949         var widths = {
49950             end: newWidth,
49951             start: this._lastWidth
49952         };
49953         
49954         this._lastVelocity = velocity;
49955         this._lastWidth = newWidth;
49956         return widths;
49957     },
49958     
49959     drawDot: function (_a) {
49960         var color = _a.color, point = _a.point;
49961         var ctx = this.canvasElCtx();
49962         var width = typeof this.dot_size === 'function' ? this.dot_size() : this.dot_size;
49963         ctx.beginPath();
49964         this.drawCurveSegment(point.x, point.y, width);
49965         ctx.closePath();
49966         ctx.fillStyle = color;
49967         ctx.fill();
49968     },
49969     
49970     drawCurve: function (_a) {
49971         var color = _a.color, curve = _a.curve;
49972         var ctx = this.canvasElCtx();
49973         var widthDelta = curve.endWidth - curve.startWidth;
49974         var drawSteps = Math.floor(curve.length()) * 2;
49975         ctx.beginPath();
49976         ctx.fillStyle = color;
49977         for (var i = 0; i < drawSteps; i += 1) {
49978         var t = i / drawSteps;
49979         var tt = t * t;
49980         var ttt = tt * t;
49981         var u = 1 - t;
49982         var uu = u * u;
49983         var uuu = uu * u;
49984         var x = uuu * curve.startPoint.x;
49985         x += 3 * uu * t * curve.control1.x;
49986         x += 3 * u * tt * curve.control2.x;
49987         x += ttt * curve.endPoint.x;
49988         var y = uuu * curve.startPoint.y;
49989         y += 3 * uu * t * curve.control1.y;
49990         y += 3 * u * tt * curve.control2.y;
49991         y += ttt * curve.endPoint.y;
49992         var width = curve.startWidth + ttt * widthDelta;
49993         this.drawCurveSegment(x, y, width);
49994         }
49995         ctx.closePath();
49996         ctx.fill();
49997     },
49998     
49999     drawCurveSegment: function (x, y, width) {
50000         var ctx = this.canvasElCtx();
50001         ctx.moveTo(x, y);
50002         ctx.arc(x, y, width, 0, 2 * Math.PI, false);
50003         this.is_empty = false;
50004     },
50005     
50006     clear: function()
50007     {
50008         var ctx = this.canvasElCtx();
50009         var canvas = this.canvasEl().dom;
50010         ctx.fillStyle = this.bg_color;
50011         ctx.clearRect(0, 0, canvas.width, canvas.height);
50012         ctx.fillRect(0, 0, canvas.width, canvas.height);
50013         this.curve_data = [];
50014         this.reset();
50015         this.is_empty = true;
50016     },
50017     
50018     fileEl: function()
50019     {
50020         return  this.el.select('input',true).first();
50021     },
50022     
50023     canvasEl: function()
50024     {
50025         return this.el.select('canvas',true).first();
50026     },
50027     
50028     canvasElCtx: function()
50029     {
50030         return this.el.select('canvas',true).first().dom.getContext('2d');
50031     },
50032     
50033     getImage: function(type)
50034     {
50035         if(this.is_empty) {
50036             return false;
50037         }
50038         
50039         // encryption ?
50040         return this.canvasEl().dom.toDataURL('image/'+type, 1);
50041     },
50042     
50043     drawFromImage: function(img_src)
50044     {
50045         var img = new Image();
50046         
50047         img.onload = function(){
50048             this.canvasElCtx().drawImage(img, 0, 0);
50049         }.bind(this);
50050         
50051         img.src = img_src;
50052         
50053         this.is_empty = false;
50054     },
50055     
50056     selectImage: function()
50057     {
50058         this.fileEl().dom.click();
50059     },
50060     
50061     uploadImage: function(e)
50062     {
50063         var reader = new FileReader();
50064         
50065         reader.onload = function(e){
50066             var img = new Image();
50067             img.onload = function(){
50068                 this.reset();
50069                 this.canvasElCtx().drawImage(img, 0, 0);
50070             }.bind(this);
50071             img.src = e.target.result;
50072         }.bind(this);
50073         
50074         reader.readAsDataURL(e.target.files[0]);
50075     },
50076     
50077     // Bezier Point Constructor
50078     Point: (function () {
50079         function Point(x, y, time) {
50080             this.x = x;
50081             this.y = y;
50082             this.time = time || Date.now();
50083         }
50084         Point.prototype.distanceTo = function (start) {
50085             return Math.sqrt(Math.pow(this.x - start.x, 2) + Math.pow(this.y - start.y, 2));
50086         };
50087         Point.prototype.equals = function (other) {
50088             return this.x === other.x && this.y === other.y && this.time === other.time;
50089         };
50090         Point.prototype.velocityFrom = function (start) {
50091             return this.time !== start.time
50092             ? this.distanceTo(start) / (this.time - start.time)
50093             : 0;
50094         };
50095         return Point;
50096     }()),
50097     
50098     
50099     // Bezier Constructor
50100     Bezier: (function () {
50101         function Bezier(startPoint, control2, control1, endPoint, startWidth, endWidth) {
50102             this.startPoint = startPoint;
50103             this.control2 = control2;
50104             this.control1 = control1;
50105             this.endPoint = endPoint;
50106             this.startWidth = startWidth;
50107             this.endWidth = endWidth;
50108         }
50109         Bezier.fromPoints = function (points, widths, scope) {
50110             var c2 = this.calculateControlPoints(points[0], points[1], points[2], scope).c2;
50111             var c3 = this.calculateControlPoints(points[1], points[2], points[3], scope).c1;
50112             return new Bezier(points[1], c2, c3, points[2], widths.start, widths.end);
50113         };
50114         Bezier.calculateControlPoints = function (s1, s2, s3, scope) {
50115             var dx1 = s1.x - s2.x;
50116             var dy1 = s1.y - s2.y;
50117             var dx2 = s2.x - s3.x;
50118             var dy2 = s2.y - s3.y;
50119             var m1 = { x: (s1.x + s2.x) / 2.0, y: (s1.y + s2.y) / 2.0 };
50120             var m2 = { x: (s2.x + s3.x) / 2.0, y: (s2.y + s3.y) / 2.0 };
50121             var l1 = Math.sqrt(dx1 * dx1 + dy1 * dy1);
50122             var l2 = Math.sqrt(dx2 * dx2 + dy2 * dy2);
50123             var dxm = m1.x - m2.x;
50124             var dym = m1.y - m2.y;
50125             var k = l2 / (l1 + l2);
50126             var cm = { x: m2.x + dxm * k, y: m2.y + dym * k };
50127             var tx = s2.x - cm.x;
50128             var ty = s2.y - cm.y;
50129             return {
50130                 c1: new scope.Point(m1.x + tx, m1.y + ty),
50131                 c2: new scope.Point(m2.x + tx, m2.y + ty)
50132             };
50133         };
50134         Bezier.prototype.length = function () {
50135             var steps = 10;
50136             var length = 0;
50137             var px;
50138             var py;
50139             for (var i = 0; i <= steps; i += 1) {
50140                 var t = i / steps;
50141                 var cx = this.point(t, this.startPoint.x, this.control1.x, this.control2.x, this.endPoint.x);
50142                 var cy = this.point(t, this.startPoint.y, this.control1.y, this.control2.y, this.endPoint.y);
50143                 if (i > 0) {
50144                     var xdiff = cx - px;
50145                     var ydiff = cy - py;
50146                     length += Math.sqrt(xdiff * xdiff + ydiff * ydiff);
50147                 }
50148                 px = cx;
50149                 py = cy;
50150             }
50151             return length;
50152         };
50153         Bezier.prototype.point = function (t, start, c1, c2, end) {
50154             return (start * (1.0 - t) * (1.0 - t) * (1.0 - t))
50155             + (3.0 * c1 * (1.0 - t) * (1.0 - t) * t)
50156             + (3.0 * c2 * (1.0 - t) * t * t)
50157             + (end * t * t * t);
50158         };
50159         return Bezier;
50160     }()),
50161     
50162     throttleStroke: function(fn, wait) {
50163       if (wait === void 0) { wait = 250; }
50164       var previous = 0;
50165       var timeout = null;
50166       var result;
50167       var storedContext;
50168       var storedArgs;
50169       var later = function () {
50170           previous = Date.now();
50171           timeout = null;
50172           result = fn.apply(storedContext, storedArgs);
50173           if (!timeout) {
50174               storedContext = null;
50175               storedArgs = [];
50176           }
50177       };
50178       return function wrapper() {
50179           var args = [];
50180           for (var _i = 0; _i < arguments.length; _i++) {
50181               args[_i] = arguments[_i];
50182           }
50183           var now = Date.now();
50184           var remaining = wait - (now - previous);
50185           storedContext = this;
50186           storedArgs = args;
50187           if (remaining <= 0 || remaining > wait) {
50188               if (timeout) {
50189                   clearTimeout(timeout);
50190                   timeout = null;
50191               }
50192               previous = now;
50193               result = fn.apply(storedContext, storedArgs);
50194               if (!timeout) {
50195                   storedContext = null;
50196                   storedArgs = [];
50197               }
50198           }
50199           else if (!timeout) {
50200               timeout = window.setTimeout(later, remaining);
50201           }
50202           return result;
50203       };
50204   }
50205   
50206 });
50207
50208  
50209
50210  // old names for form elements
50211 Roo.bootstrap.Form          =   Roo.bootstrap.form.Form;
50212 Roo.bootstrap.Input         =   Roo.bootstrap.form.Input;
50213 Roo.bootstrap.TextArea      =   Roo.bootstrap.form.TextArea;
50214 Roo.bootstrap.TriggerField  =   Roo.bootstrap.form.TriggerField;
50215 Roo.bootstrap.ComboBox      =   Roo.bootstrap.form.ComboBox;
50216 Roo.bootstrap.DateField     =   Roo.bootstrap.form.DateField;
50217 Roo.bootstrap.TimeField     =   Roo.bootstrap.form.TimeField;
50218 Roo.bootstrap.MonthField    =   Roo.bootstrap.form.MonthField;
50219 Roo.bootstrap.CheckBox      =   Roo.bootstrap.form.CheckBox;
50220 Roo.bootstrap.Radio         =   Roo.bootstrap.form.Radio;
50221 Roo.bootstrap.RadioSet      =   Roo.bootstrap.form.RadioSet;
50222 Roo.bootstrap.SecurePass    =   Roo.bootstrap.form.SecurePass;
50223 Roo.bootstrap.FieldLabel    =   Roo.bootstrap.form.FieldLabel;
50224 Roo.bootstrap.DateSplitField=   Roo.bootstrap.form.DateSplitField;
50225 Roo.bootstrap.NumberField   =   Roo.bootstrap.form.NumberField;
50226 Roo.bootstrap.PhoneInput    =   Roo.bootstrap.form.PhoneInput;
50227 Roo.bootstrap.PhoneInputData=   Roo.bootstrap.form.PhoneInputData;
50228 Roo.bootstrap.MoneyField    =   Roo.bootstrap.form.MoneyField;
50229 Roo.bootstrap.HtmlEditor    =   Roo.bootstrap.form.HtmlEditor;
50230 Roo.bootstrap.HtmlEditor.ToolbarStandard =   Roo.bootstrap.form.HtmlEditorToolbarStandard;
50231 Roo.bootstrap.Markdown      = Roo.bootstrap.form.Markdown;
50232 Roo.bootstrap.CardUploader  = Roo.bootstrap.form.CardUploader;// depricated.
50233 Roo.bootstrap.Navbar            = Roo.bootstrap.nav.Bar;
50234 Roo.bootstrap.NavGroup          = Roo.bootstrap.nav.Group;
50235 Roo.bootstrap.NavHeaderbar      = Roo.bootstrap.nav.Headerbar;
50236 Roo.bootstrap.NavItem           = Roo.bootstrap.nav.Item;
50237
50238 Roo.bootstrap.NavProgressBar     = Roo.bootstrap.nav.ProgressBar;
50239 Roo.bootstrap.NavProgressBarItem = Roo.bootstrap.nav.ProgressBarItem;
50240
50241 Roo.bootstrap.NavSidebar        = Roo.bootstrap.nav.Sidebar;
50242 Roo.bootstrap.NavSidebarItem    = Roo.bootstrap.nav.SidebarItem;
50243
50244 Roo.bootstrap.NavSimplebar      = Roo.bootstrap.nav.Simplebar;// deprciated 
50245 Roo.bootstrap.Menu = Roo.bootstrap.menu.Menu;
50246 Roo.bootstrap.MenuItem =  Roo.bootstrap.menu.Item;
50247 Roo.bootstrap.MenuSeparator = Roo.bootstrap.menu.Separator
50248