Roo/bootstrap/Table.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  * Based on:
22  * Ext JS Library 1.1.1
23  * Copyright(c) 2006-2007, Ext JS, LLC.
24  *
25  * Originally Released Under LGPL - original licence link has changed is not relivant.
26  *
27  * Fork - LGPL
28  * <script type="text/javascript">
29  */
30
31
32 /**
33  * @class Roo.Shadow
34  * Simple class that can provide a shadow effect for any element.  Note that the element MUST be absolutely positioned,
35  * and the shadow does not provide any shimming.  This should be used only in simple cases -- for more advanced
36  * functionality that can also provide the same shadow effect, see the {@link Roo.Layer} class.
37  * @constructor
38  * Create a new Shadow
39  * @param {Object} config The config object
40  */
41 Roo.Shadow = function(config){
42     Roo.apply(this, config);
43     if(typeof this.mode != "string"){
44         this.mode = this.defaultMode;
45     }
46     var o = this.offset, a = {h: 0};
47     var rad = Math.floor(this.offset/2);
48     switch(this.mode.toLowerCase()){ // all this hideous nonsense calculates the various offsets for shadows
49         case "drop":
50             a.w = 0;
51             a.l = a.t = o;
52             a.t -= 1;
53             if(Roo.isIE){
54                 a.l -= this.offset + rad;
55                 a.t -= this.offset + rad;
56                 a.w -= rad;
57                 a.h -= rad;
58                 a.t += 1;
59             }
60         break;
61         case "sides":
62             a.w = (o*2);
63             a.l = -o;
64             a.t = o-1;
65             if(Roo.isIE){
66                 a.l -= (this.offset - rad);
67                 a.t -= this.offset + rad;
68                 a.l += 1;
69                 a.w -= (this.offset - rad)*2;
70                 a.w -= rad + 1;
71                 a.h -= 1;
72             }
73         break;
74         case "frame":
75             a.w = a.h = (o*2);
76             a.l = a.t = -o;
77             a.t += 1;
78             a.h -= 2;
79             if(Roo.isIE){
80                 a.l -= (this.offset - rad);
81                 a.t -= (this.offset - rad);
82                 a.l += 1;
83                 a.w -= (this.offset + rad + 1);
84                 a.h -= (this.offset + rad);
85                 a.h += 1;
86             }
87         break;
88     };
89
90     this.adjusts = a;
91 };
92
93 Roo.Shadow.prototype = {
94     /**
95      * @cfg {String} mode
96      * The shadow display mode.  Supports the following options:<br />
97      * sides: Shadow displays on both sides and bottom only<br />
98      * frame: Shadow displays equally on all four sides<br />
99      * drop: Traditional bottom-right drop shadow (default)
100      */
101     mode: false,
102     /**
103      * @cfg {String} offset
104      * The number of pixels to offset the shadow from the element (defaults to 4)
105      */
106     offset: 4,
107
108     // private
109     defaultMode: "drop",
110
111     /**
112      * Displays the shadow under the target element
113      * @param {String/HTMLElement/Element} targetEl The id or element under which the shadow should display
114      */
115     show : function(target){
116         target = Roo.get(target);
117         if(!this.el){
118             this.el = Roo.Shadow.Pool.pull();
119             if(this.el.dom.nextSibling != target.dom){
120                 this.el.insertBefore(target);
121             }
122         }
123         this.el.setStyle("z-index", this.zIndex || parseInt(target.getStyle("z-index"), 10)-1);
124         if(Roo.isIE){
125             this.el.dom.style.filter="progid:DXImageTransform.Microsoft.alpha(opacity=50) progid:DXImageTransform.Microsoft.Blur(pixelradius="+(this.offset)+")";
126         }
127         this.realign(
128             target.getLeft(true),
129             target.getTop(true),
130             target.getWidth(),
131             target.getHeight()
132         );
133         this.el.dom.style.display = "block";
134     },
135
136     /**
137      * Returns true if the shadow is visible, else false
138      */
139     isVisible : function(){
140         return this.el ? true : false;  
141     },
142
143     /**
144      * Direct alignment when values are already available. Show must be called at least once before
145      * calling this method to ensure it is initialized.
146      * @param {Number} left The target element left position
147      * @param {Number} top The target element top position
148      * @param {Number} width The target element width
149      * @param {Number} height The target element height
150      */
151     realign : function(l, t, w, h){
152         if(!this.el){
153             return;
154         }
155         var a = this.adjusts, d = this.el.dom, s = d.style;
156         var iea = 0;
157         s.left = (l+a.l)+"px";
158         s.top = (t+a.t)+"px";
159         var sw = (w+a.w), sh = (h+a.h), sws = sw +"px", shs = sh + "px";
160  
161         if(s.width != sws || s.height != shs){
162             s.width = sws;
163             s.height = shs;
164             if(!Roo.isIE){
165                 var cn = d.childNodes;
166                 var sww = Math.max(0, (sw-12))+"px";
167                 cn[0].childNodes[1].style.width = sww;
168                 cn[1].childNodes[1].style.width = sww;
169                 cn[2].childNodes[1].style.width = sww;
170                 cn[1].style.height = Math.max(0, (sh-12))+"px";
171             }
172         }
173     },
174
175     /**
176      * Hides this shadow
177      */
178     hide : function(){
179         if(this.el){
180             this.el.dom.style.display = "none";
181             Roo.Shadow.Pool.push(this.el);
182             delete this.el;
183         }
184     },
185
186     /**
187      * Adjust the z-index of this shadow
188      * @param {Number} zindex The new z-index
189      */
190     setZIndex : function(z){
191         this.zIndex = z;
192         if(this.el){
193             this.el.setStyle("z-index", z);
194         }
195     }
196 };
197
198 // Private utility class that manages the internal Shadow cache
199 Roo.Shadow.Pool = function(){
200     var p = [];
201     var markup = Roo.isIE ?
202                  '<div class="x-ie-shadow"></div>' :
203                  '<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>';
204     return {
205         pull : function(){
206             var sh = p.shift();
207             if(!sh){
208                 sh = Roo.get(Roo.DomHelper.insertHtml("beforeBegin", document.body.firstChild, markup));
209                 sh.autoBoxAdjust = false;
210             }
211             return sh;
212         },
213
214         push : function(sh){
215             p.push(sh);
216         }
217     };
218 }();/*
219  * - LGPL
220  *
221  * base class for bootstrap elements.
222  * 
223  */
224
225 Roo.bootstrap = Roo.bootstrap || {};
226 /**
227  * @class Roo.bootstrap.Component
228  * @extends Roo.Component
229  * @abstract
230  * @children Roo.bootstrap.Component
231  * Bootstrap Component base class
232  * @cfg {String} cls css class
233  * @cfg {String} style any extra css
234  * @cfg {Object} xattr extra attributes to add to 'element' (used by builder to store stuff.)
235  * @cfg {Boolean} can_build_overlaid  True if element can be rebuild from a HTML page
236  * @cfg {string} dataId cutomer id
237  * @cfg {string} name Specifies name attribute
238  * @cfg {string} tooltip  Text for the tooltip
239  * @cfg {string} container_method method to fetch parents container element (used by NavHeaderbar -  getHeaderChildContainer)
240  * @cfg {string|object} visibilityEl (el|parent) What element to use for visibility (@see getVisibilityEl())
241  
242  * @constructor
243  * Do not use directly - it does not do anything..
244  * @param {Object} config The config object
245  */
246
247
248
249 Roo.bootstrap.Component = function(config){
250     Roo.bootstrap.Component.superclass.constructor.call(this, config);
251        
252     this.addEvents({
253         /**
254          * @event childrenrendered
255          * Fires when the children have been rendered..
256          * @param {Roo.bootstrap.Component} this
257          */
258         "childrenrendered" : true
259         
260         
261         
262     });
263     
264     
265 };
266
267 Roo.extend(Roo.bootstrap.Component, Roo.BoxComponent,  {
268     
269     
270     allowDomMove : false, // to stop relocations in parent onRender...
271     
272     cls : false,
273     
274     style : false,
275     
276     autoCreate : false,
277     
278     tooltip : null,
279     /**
280      * Initialize Events for the element
281      */
282     initEvents : function() { },
283     
284     xattr : false,
285     
286     parentId : false,
287     
288     can_build_overlaid : true,
289     
290     container_method : false,
291     
292     dataId : false,
293     
294     name : false,
295     
296     parent: function() {
297         // returns the parent component..
298         return Roo.ComponentMgr.get(this.parentId)
299         
300         
301     },
302     
303     // private
304     onRender : function(ct, position)
305     {
306        // Roo.log("Call onRender: " + this.xtype);
307         
308         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
309         
310         if(this.el){
311             if (this.el.attr('xtype')) {
312                 this.el.attr('xtypex', this.el.attr('xtype'));
313                 this.el.dom.removeAttribute('xtype');
314                 
315                 this.initEvents();
316             }
317             
318             return;
319         }
320         
321          
322         
323         var cfg = Roo.apply({},  this.getAutoCreate());
324         
325         cfg.id = this.id || Roo.id();
326         
327         // fill in the extra attributes 
328         if (this.xattr && typeof(this.xattr) =='object') {
329             for (var i in this.xattr) {
330                 cfg[i] = this.xattr[i];
331             }
332         }
333         
334         if(this.dataId){
335             cfg.dataId = this.dataId;
336         }
337         
338         if (this.cls) {
339             cfg.cls = (typeof(cfg.cls) == 'undefined' ? this.cls : cfg.cls) + ' ' + this.cls;
340         }
341         
342         if (this.style) { // fixme needs to support more complex style data.
343             cfg.style = (typeof(cfg.style) == 'undefined' ? this.style : cfg.style) + '; ' + this.style;
344         }
345         
346         if(this.name){
347             cfg.name = this.name;
348         }
349         
350         this.el = ct.createChild(cfg, position);
351         
352         if (this.tooltip) {
353             this.tooltipEl().attr('tooltip', this.tooltip);
354         }
355         
356         if(this.tabIndex !== undefined){
357             this.el.dom.setAttribute('tabIndex', this.tabIndex);
358         }
359         
360         this.initEvents();
361         
362     },
363     /**
364      * Fetch the element to add children to
365      * @return {Roo.Element} defaults to this.el
366      */
367     getChildContainer : function()
368     {
369         return this.el;
370     },
371     getDocumentBody : function() // used by menus - as they are attached to the body so zIndexes work
372     {
373         return Roo.get(document.body);
374     },
375     
376     /**
377      * Fetch the element to display the tooltip on.
378      * @return {Roo.Element} defaults to this.el
379      */
380     tooltipEl : function()
381     {
382         return this.el;
383     },
384         
385     addxtype  : function(tree,cntr)
386     {
387         var cn = this;
388         
389         cn = Roo.factory(tree);
390         //Roo.log(['addxtype', cn]);
391            
392         cn.parentType = this.xtype; //??
393         cn.parentId = this.id;
394         
395         cntr = (typeof(cntr) == 'undefined' ) ? 'getChildContainer' : cntr;
396         if (typeof(cn.container_method) == 'string') {
397             cntr = cn.container_method;
398         }
399         
400         
401         var has_flexy_each =  (typeof(tree['flexy:foreach']) != 'undefined');
402         
403         var has_flexy_if =  (typeof(tree['flexy:if']) != 'undefined');
404         
405         var build_from_html =  Roo.XComponent.build_from_html;
406           
407         var is_body  = (tree.xtype == 'Body') ;
408           
409         var page_has_body = (Roo.get(document.body).attr('xtype') == 'Roo.bootstrap.Body');
410           
411         var self_cntr_el = Roo.get(this[cntr](false));
412         
413         // do not try and build conditional elements 
414         if ((has_flexy_each || has_flexy_if || this.can_build_overlaid == false ) && build_from_html) {
415             return false;
416         }
417         
418         if (!has_flexy_each || !build_from_html || is_body || !page_has_body) {
419             if(!has_flexy_if || typeof(tree.name) == 'undefined' || !build_from_html || is_body || !page_has_body){
420                 return this.addxtypeChild(tree,cntr, is_body);
421             }
422             
423             var echild =self_cntr_el ? self_cntr_el.child('>*[name=' + tree.name + ']') : false;
424                 
425             if(echild){
426                 return this.addxtypeChild(Roo.apply({}, tree),cntr);
427             }
428             
429             Roo.log('skipping render');
430             return cn;
431             
432         }
433         
434         var ret = false;
435         if (!build_from_html) {
436             return false;
437         }
438         
439         // this i think handles overlaying multiple children of the same type
440         // with the sam eelement.. - which might be buggy..
441         while (true) {
442             var echild =self_cntr_el ? self_cntr_el.child('>*[xtype]') : false;
443             
444             if (!echild) {
445                 break;
446             }
447             
448             if (echild && echild.attr('xtype').split('.').pop() != cn.xtype) {
449                 break;
450             }
451             
452             ret = this.addxtypeChild(Roo.apply({}, tree),cntr);
453         }
454        
455         return ret;
456     },
457     
458     
459     addxtypeChild : function (tree, cntr, is_body)
460     {
461         Roo.debug && Roo.log('addxtypeChild:' + cntr);
462         var cn = this;
463         cntr = (typeof(cntr) == 'undefined' ) ? 'getChildContainer' : cntr;
464         
465         
466         var has_flexy = (typeof(tree['flexy:if']) != 'undefined') ||
467                     (typeof(tree['flexy:foreach']) != 'undefined');
468           
469     
470         
471         skip_children = false;
472         // render the element if it's not BODY.
473         if (!is_body) {
474             
475             // if parent was disabled, then do not try and create the children..
476             if(!this[cntr](true)){
477                 tree.items = [];
478                 return tree;
479             }
480            
481             cn = Roo.factory(tree);
482            
483             cn.parentType = this.xtype; //??
484             cn.parentId = this.id;
485             
486             var build_from_html =  Roo.XComponent.build_from_html;
487             
488             
489             // does the container contain child eleemnts with 'xtype' attributes.
490             // that match this xtype..
491             // note - when we render we create these as well..
492             // so we should check to see if body has xtype set.
493             if (build_from_html && Roo.get(document.body).attr('xtype') == 'Roo.bootstrap.Body') {
494                
495                 var self_cntr_el = Roo.get(this[cntr](false));
496                 var echild =self_cntr_el ? self_cntr_el.child('>*[xtype]') : false;
497                 if (echild) { 
498                     //Roo.log(Roo.XComponent.build_from_html);
499                     //Roo.log("got echild:");
500                     //Roo.log(echild);
501                 }
502                 // there is a scenario where some of the child elements are flexy:if (and all of the same type)
503                 // and are not displayed -this causes this to use up the wrong element when matching.
504                 // at present the only work around for this is to nest flexy:if elements in another element that is always rendered.
505                 
506                 
507                 if (echild && echild.attr('xtype').split('.').pop() == cn.xtype) {
508                   //  Roo.log("found child for " + this.xtype +": " + echild.attr('xtype') );
509                   
510                   
511                   
512                     cn.el = echild;
513                   //  Roo.log("GOT");
514                     //echild.dom.removeAttribute('xtype');
515                 } else {
516                     Roo.debug && Roo.log("MISSING " + cn.xtype + " on child of " + (this.el ? this.el.attr('xbuilderid') : 'no parent'));
517                     Roo.debug && Roo.log(self_cntr_el);
518                     Roo.debug && Roo.log(echild);
519                     Roo.debug && Roo.log(cn);
520                 }
521             }
522            
523             
524            
525             // if object has flexy:if - then it may or may not be rendered.
526             if (build_from_html && has_flexy && !cn.el &&  cn.can_build_overlaid) {
527                 // skip a flexy if element.
528                 Roo.debug && Roo.log('skipping render');
529                 Roo.debug && Roo.log(tree);
530                 if (!cn.el) {
531                     Roo.debug && Roo.log('skipping all children');
532                     skip_children = true;
533                 }
534                 
535              } else {
536                  
537                 // actually if flexy:foreach is found, we really want to create 
538                 // multiple copies here...
539                 //Roo.log('render');
540                 //Roo.log(this[cntr]());
541                 // some elements do not have render methods.. like the layouts...
542                 /*
543                 if(this[cntr](true) === false){
544                     cn.items = [];
545                     return cn;
546                 }
547                 */
548                 cn.render && cn.render(this[cntr](true));
549                 
550              }
551             // then add the element..
552         }
553          
554         // handle the kids..
555         
556         var nitems = [];
557         /*
558         if (typeof (tree.menu) != 'undefined') {
559             tree.menu.parentType = cn.xtype;
560             tree.menu.triggerEl = cn.el;
561             nitems.push(cn.addxtype(Roo.apply({}, tree.menu)));
562             
563         }
564         */
565         if (!tree.items || !tree.items.length) {
566             cn.items = nitems;
567             //Roo.log(["no children", this]);
568             
569             return cn;
570         }
571          
572         var items = tree.items;
573         delete tree.items;
574         
575         //Roo.log(items.length);
576             // add the items..
577         if (!skip_children) {    
578             for(var i =0;i < items.length;i++) {
579               //  Roo.log(['add child', items[i]]);
580                 nitems.push(cn.addxtype(Roo.apply({}, items[i])));
581             }
582         }
583         
584         cn.items = nitems;
585         
586         //Roo.log("fire childrenrendered");
587         
588         cn.fireEvent('childrenrendered', this);
589         
590         return cn;
591     },
592     
593     /**
594      * Set the element that will be used to show or hide
595      */
596     setVisibilityEl : function(el)
597     {
598         this.visibilityEl = el;
599     },
600     
601      /**
602      * Get the element that will be used to show or hide
603      */
604     getVisibilityEl : function()
605     {
606         if (typeof(this.visibilityEl) == 'object') {
607             return this.visibilityEl;
608         }
609         
610         if (typeof(this.visibilityEl) == 'string') {
611             return this.visibilityEl == 'parent' ? this.parent().getEl() : this.getEl();
612         }
613         
614         return this.getEl();
615     },
616     
617     /**
618      * Show a component - removes 'hidden' class
619      */
620     show : function()
621     {
622         if(!this.getVisibilityEl()){
623             return;
624         }
625          
626         this.getVisibilityEl().removeClass(['hidden','d-none']);
627         
628         this.fireEvent('show', this);
629         
630         
631     },
632     /**
633      * Hide a component - adds 'hidden' class
634      */
635     hide: function()
636     {
637         if(!this.getVisibilityEl()){
638             return;
639         }
640         
641         this.getVisibilityEl().addClass(['hidden','d-none']);
642         
643         this.fireEvent('hide', this);
644         
645     }
646 });
647
648  /*
649  * - LGPL
650  *
651  * element
652  * 
653  */
654
655 /**
656  * @class Roo.bootstrap.Element
657  * @extends Roo.bootstrap.Component
658  * @children Roo.bootstrap.Component
659  * Bootstrap Element class (basically a DIV used to make random stuff )
660  * 
661  * @cfg {String} html contents of the element
662  * @cfg {String} tag tag of the element
663  * @cfg {String} cls class of the element
664  * @cfg {Boolean} preventDefault (true|false) default false
665  * @cfg {Boolean} clickable (true|false) default false
666  * @cfg {String} role default blank - set to button to force cursor pointer
667  
668  * 
669  * @constructor
670  * Create a new Element
671  * @param {Object} config The config object
672  */
673
674 Roo.bootstrap.Element = function(config){
675     Roo.bootstrap.Element.superclass.constructor.call(this, config);
676     
677     this.addEvents({
678         // raw events
679         /**
680          * @event click
681          * When a element is chick
682          * @param {Roo.bootstrap.Element} this
683          * @param {Roo.EventObject} e
684          */
685         "click" : true 
686         
687       
688     });
689 };
690
691 Roo.extend(Roo.bootstrap.Element, Roo.bootstrap.Component,  {
692     
693     tag: 'div',
694     cls: '',
695     html: '',
696     preventDefault: false, 
697     clickable: false,
698     tapedTwice : false,
699     role : false,
700     
701     getAutoCreate : function(){
702         
703         var cfg = {
704             tag: this.tag,
705             // cls: this.cls, double assign in parent class Component.js :: onRender
706             html: this.html
707         };
708         if (this.role !== false) {
709             cfg.role = this.role;
710         }
711         
712         return cfg;
713     },
714     
715     initEvents: function() 
716     {
717         Roo.bootstrap.Element.superclass.initEvents.call(this);
718         
719         if(this.clickable){
720             this.el.on('click', this.onClick, this);
721         }
722         
723         
724     },
725     
726     onClick : function(e)
727     {
728         if(this.preventDefault){
729             e.preventDefault();
730         }
731         
732         this.fireEvent('click', this, e); // why was this double click before?
733     },
734     
735     
736     
737
738     
739     
740     getValue : function()
741     {
742         return this.el.dom.innerHTML;
743     },
744     
745     setValue : function(value)
746     {
747         this.el.dom.innerHTML = value;
748     }
749    
750 });
751
752  
753
754  /*
755  * - LGPL
756  *
757  * dropable area
758  * 
759  */
760
761 /**
762  * @class Roo.bootstrap.DropTarget
763  * @extends Roo.bootstrap.Element
764  * Bootstrap DropTarget class
765  
766  * @cfg {string} name dropable name
767  * 
768  * @constructor
769  * Create a new Dropable Area
770  * @param {Object} config The config object
771  */
772
773 Roo.bootstrap.DropTarget = function(config){
774     Roo.bootstrap.DropTarget.superclass.constructor.call(this, config);
775     
776     this.addEvents({
777         // raw events
778         /**
779          * @event click
780          * When a element is chick
781          * @param {Roo.bootstrap.Element} this
782          * @param {Roo.EventObject} e
783          */
784         "drop" : true
785     });
786 };
787
788 Roo.extend(Roo.bootstrap.DropTarget, Roo.bootstrap.Element,  {
789     
790     
791     getAutoCreate : function(){
792         
793          
794     },
795     
796     initEvents: function() 
797     {
798         Roo.bootstrap.DropTarget.superclass.initEvents.call(this);
799         this.dropZone = new Roo.dd.DropTarget(this.getEl(), {
800             ddGroup: this.name,
801             listeners : {
802                 drop : this.dragDrop.createDelegate(this),
803                 enter : this.dragEnter.createDelegate(this),
804                 out : this.dragOut.createDelegate(this),
805                 over : this.dragOver.createDelegate(this)
806             }
807             
808         });
809         this.dropZone.DDM.useCache = false // so data gets refreshed when we resize stuff
810     },
811     
812     dragDrop : function(source,e,data)
813     {
814         // user has to decide how to impliment this.
815         Roo.log('drop');
816         Roo.log(this);
817         //this.fireEvent('drop', this, source, e ,data);
818         return false;
819     },
820     
821     dragEnter : function(n, dd, e, data)
822     {
823         // probably want to resize the element to match the dropped element..
824         Roo.log("enter");
825         this.originalSize = this.el.getSize();
826         this.el.setSize( n.el.getSize());
827         this.dropZone.DDM.refreshCache(this.name);
828         Roo.log([n, dd, e, data]);
829     },
830     
831     dragOut : function(value)
832     {
833         // resize back to normal
834         Roo.log("out");
835         this.el.setSize(this.originalSize);
836         this.dropZone.resetConstraints();
837     },
838     
839     dragOver : function()
840     {
841         // ??? do nothing?
842     }
843    
844 });
845
846  
847
848  /*
849  * - LGPL
850  *
851  * Body
852  *
853  */
854
855 /**
856  * @class Roo.bootstrap.Body
857  * @extends Roo.bootstrap.Component
858  * @children Roo.bootstrap.Component 
859  * @parent none builder
860  * Bootstrap Body class
861  *
862  * @constructor
863  * Create a new body
864  * @param {Object} config The config object
865  */
866
867 Roo.bootstrap.Body = function(config){
868
869     config = config || {};
870
871     Roo.bootstrap.Body.superclass.constructor.call(this, config);
872     this.el = Roo.get(config.el ? config.el : document.body );
873     if (this.cls && this.cls.length) {
874         Roo.get(document.body).addClass(this.cls);
875     }
876 };
877
878 Roo.extend(Roo.bootstrap.Body, Roo.bootstrap.Component,  {
879
880     is_body : true,// just to make sure it's constructed?
881
882         autoCreate : {
883         cls: 'container'
884     },
885     onRender : function(ct, position)
886     {
887        /* Roo.log("Roo.bootstrap.Body - onRender");
888         if (this.cls && this.cls.length) {
889             Roo.get(document.body).addClass(this.cls);
890         }
891         // style??? xttr???
892         */
893     }
894
895
896
897
898 });
899 /*
900  * - LGPL
901  *
902  * button group
903  * 
904  */
905
906
907 /**
908  * @class Roo.bootstrap.ButtonGroup
909  * @extends Roo.bootstrap.Component
910  * Bootstrap ButtonGroup class
911  * @children Roo.bootstrap.Button Roo.bootstrap.form.Form
912  * 
913  * @cfg {String} size lg | sm | xs (default empty normal)
914  * @cfg {String} align vertical | justified  (default none)
915  * @cfg {String} direction up | down (default down)
916  * @cfg {Boolean} toolbar false | true
917  * @cfg {Boolean} btn true | false
918  * 
919  * 
920  * @constructor
921  * Create a new Input
922  * @param {Object} config The config object
923  */
924
925 Roo.bootstrap.ButtonGroup = function(config){
926     Roo.bootstrap.ButtonGroup.superclass.constructor.call(this, config);
927 };
928
929 Roo.extend(Roo.bootstrap.ButtonGroup, Roo.bootstrap.Component,  {
930     
931     size: '',
932     align: '',
933     direction: '',
934     toolbar: false,
935     btn: true,
936
937     getAutoCreate : function(){
938         var cfg = {
939             cls: 'btn-group',
940             html : null
941         };
942         
943         cfg.html = this.html || cfg.html;
944         
945         if (this.toolbar) {
946             cfg = {
947                 cls: 'btn-toolbar',
948                 html: null
949             };
950             
951             return cfg;
952         }
953         
954         if (['vertical','justified'].indexOf(this.align)!==-1) {
955             cfg.cls = 'btn-group-' + this.align;
956             
957             if (this.align == 'justified') {
958                 console.log(this.items);
959             }
960         }
961         
962         if (['lg','sm','xs'].indexOf(this.size)!==-1) {
963             cfg.cls += ' btn-group-' + this.size;
964         }
965         
966         if (this.direction == 'up') {
967             cfg.cls += ' dropup' ;
968         }
969         
970         return cfg;
971     },
972     /**
973      * Add a button to the group (similar to NavItem API.)
974      */
975     addItem : function(cfg)
976     {
977         var cn = new Roo.bootstrap.Button(cfg);
978         //this.register(cn);
979         cn.parentId = this.id;
980         cn.onRender(this.el, null);
981         return cn;
982     }
983    
984 });
985
986  /*
987  * - LGPL
988  *
989  * button
990  * 
991  */
992
993 /**
994  * @class Roo.bootstrap.Button
995  * @extends Roo.bootstrap.Component
996  * Bootstrap Button class
997  * @cfg {String} html The button content
998  * @cfg {String} weight (default|primary|secondary|success|info|warning|danger|link|light|dark) default
999  * @cfg {String} badge_weight (default|primary|secondary|success|info|warning|danger|link|light|dark) default (same as button)
1000  * @cfg {Boolean} outline default false (except for weight=default which emulates old behaveiour with an outline)
1001  * @cfg {String} size (lg|sm|xs)
1002  * @cfg {String} tag (a|input|submit)
1003  * @cfg {String} href empty or href
1004  * @cfg {Boolean} disabled default false;
1005  * @cfg {Boolean} isClose default false;
1006  * @cfg {String} glyphicon depricated - use fa
1007  * @cfg {String} fa fontawesome icon - eg. 'comment' - without the fa/fas etc..
1008  * @cfg {String} badge text for badge
1009  * @cfg {String} theme (default|glow)  
1010  * @cfg {Boolean} inverse dark themed version
1011  * @cfg {Boolean} toggle is it a slidy toggle button
1012  * @cfg {Boolean} pressed   default null - if the button ahs active state
1013  * @cfg {String} ontext text for on slidy toggle state
1014  * @cfg {String} offtext text for off slidy toggle state
1015  * @cfg {Boolean} preventDefault  default true (stop click event triggering the URL if it's a link.)
1016  * @cfg {Boolean} removeClass remove the standard class..
1017  * @cfg {String} target (_self|_blank|_parent|_top|other) target for a href. 
1018  * @cfg {Boolean} grpup if parent is a btn group - then it turns it into a toogleGroup.
1019  * @cfg {Roo.bootstrap.menu.Menu} menu a Menu 
1020
1021  * @constructor
1022  * Create a new button
1023  * @param {Object} config The config object
1024  */
1025
1026
1027 Roo.bootstrap.Button = function(config){
1028     Roo.bootstrap.Button.superclass.constructor.call(this, config);
1029     
1030     this.addEvents({
1031         // raw events
1032         /**
1033          * @event click
1034          * When a button is pressed
1035          * @param {Roo.bootstrap.Button} btn
1036          * @param {Roo.EventObject} e
1037          */
1038         "click" : true,
1039         /**
1040          * @event dblclick
1041          * When a button is double clicked
1042          * @param {Roo.bootstrap.Button} btn
1043          * @param {Roo.EventObject} e
1044          */
1045         "dblclick" : true,
1046          /**
1047          * @event toggle
1048          * After the button has been toggles
1049          * @param {Roo.bootstrap.Button} btn
1050          * @param {Roo.EventObject} e
1051          * @param {boolean} pressed (also available as button.pressed)
1052          */
1053         "toggle" : true
1054     });
1055 };
1056
1057 Roo.extend(Roo.bootstrap.Button, Roo.bootstrap.Component,  {
1058     html: false,
1059     active: false,
1060     weight: '',
1061     badge_weight: '',
1062     outline : false,
1063     size: '',
1064     tag: 'button',
1065     href: '',
1066     disabled: false,
1067     isClose: false,
1068     glyphicon: '',
1069     fa: '',
1070     badge: '',
1071     theme: 'default',
1072     inverse: false,
1073     
1074     toggle: false,
1075     ontext: 'ON',
1076     offtext: 'OFF',
1077     defaulton: true,
1078     preventDefault: true,
1079     removeClass: false,
1080     name: false,
1081     target: false,
1082     group : false,
1083      
1084     pressed : null,
1085      
1086     
1087     getAutoCreate : function(){
1088         
1089         var cfg = {
1090             tag : 'button',
1091             cls : 'roo-button',
1092             html: ''
1093         };
1094         
1095         if (['a', 'button', 'input', 'submit'].indexOf(this.tag) < 0) {
1096             throw "Invalid value for tag: " + this.tag + ". must be a, button, input or submit.";
1097             this.tag = 'button';
1098         } else {
1099             cfg.tag = this.tag;
1100         }
1101         cfg.html = '<span class="roo-button-text">' + (this.html || cfg.html) + '</span>';
1102         
1103         if (this.toggle == true) {
1104             cfg={
1105                 tag: 'div',
1106                 cls: 'slider-frame roo-button',
1107                 cn: [
1108                     {
1109                         tag: 'span',
1110                         'data-on-text':'ON',
1111                         'data-off-text':'OFF',
1112                         cls: 'slider-button',
1113                         html: this.offtext
1114                     }
1115                 ]
1116             };
1117             // why are we validating the weights?
1118             if (Roo.bootstrap.Button.weights.indexOf(this.weight) > -1) {
1119                 cfg.cls +=  ' ' + this.weight;
1120             }
1121             
1122             return cfg;
1123         }
1124         
1125         if (this.isClose) {
1126             cfg.cls += ' close';
1127             
1128             cfg["aria-hidden"] = true;
1129             
1130             cfg.html = "&times;";
1131             
1132             return cfg;
1133         }
1134              
1135         
1136         if (this.theme==='default') {
1137             cfg.cls = 'btn roo-button';
1138             
1139             //if (this.parentType != 'Navbar') {
1140             this.weight = this.weight.length ?  this.weight : 'default';
1141             //}
1142             if (Roo.bootstrap.Button.weights.indexOf(this.weight) > -1) {
1143                 
1144                 var outline = this.outline || this.weight == 'default' ? 'outline-' : '';
1145                 var weight = this.weight == 'default' ? 'secondary' : this.weight;
1146                 cfg.cls += ' btn-' + outline + weight;
1147                 if (this.weight == 'default') {
1148                     // BC
1149                     cfg.cls += ' btn-' + this.weight;
1150                 }
1151             }
1152         } else if (this.theme==='glow') {
1153             
1154             cfg.tag = 'a';
1155             cfg.cls = 'btn-glow roo-button';
1156             
1157             if (Roo.bootstrap.Button.weights.indexOf(this.weight) > -1) {
1158                 
1159                 cfg.cls += ' ' + this.weight;
1160             }
1161         }
1162    
1163         
1164         if (this.inverse) {
1165             this.cls += ' inverse';
1166         }
1167         
1168         
1169         if (this.active || this.pressed === true) {
1170             cfg.cls += ' active';
1171         }
1172         
1173         if (this.disabled) {
1174             cfg.disabled = 'disabled';
1175         }
1176         
1177         if (this.items) {
1178             Roo.log('changing to ul' );
1179             cfg.tag = 'ul';
1180             this.glyphicon = 'caret';
1181             if (Roo.bootstrap.version == 4) {
1182                 this.fa = 'caret-down';
1183             }
1184             
1185         }
1186         
1187         cfg.cls += this.size.length ? (' btn-' + this.size) : '';
1188          
1189         //gsRoo.log(this.parentType);
1190         if (this.parentType === 'Navbar' && !this.parent().bar) {
1191             Roo.log('changing to li?');
1192             
1193             cfg.tag = 'li';
1194             
1195             cfg.cls = '';
1196             cfg.cn =  [{
1197                 tag : 'a',
1198                 cls : 'roo-button',
1199                 html : this.html,
1200                 href : this.href || '#'
1201             }];
1202             if (this.menu) {
1203                 cfg.cn[0].html = this.html  + ' <span class="caret"></span>';
1204                 cfg.cls += ' dropdown';
1205             }   
1206             
1207             delete cfg.html;
1208             
1209         }
1210         
1211        cfg.cls += this.parentType === 'Navbar' ?  ' navbar-btn' : '';
1212         
1213         if (this.glyphicon) {
1214             cfg.html = ' ' + cfg.html;
1215             
1216             cfg.cn = [
1217                 {
1218                     tag: 'span',
1219                     cls: 'glyphicon glyphicon-' + this.glyphicon
1220                 }
1221             ];
1222         }
1223         if (this.fa) {
1224             cfg.html = ' ' + cfg.html;
1225             
1226             cfg.cn = [
1227                 {
1228                     tag: 'i',
1229                     cls: 'fa fas fa-' + this.fa
1230                 }
1231             ];
1232         }
1233         
1234         if (this.badge) {
1235             cfg.html += ' ';
1236             
1237             cfg.tag = 'a';
1238             
1239 //            cfg.cls='btn roo-button';
1240             
1241             cfg.href=this.href;
1242             
1243             var value = cfg.html;
1244             
1245             if(this.glyphicon){
1246                 value = {
1247                     tag: 'span',
1248                     cls: 'glyphicon glyphicon-' + this.glyphicon,
1249                     html: this.html
1250                 };
1251             }
1252             if(this.fa){
1253                 value = {
1254                     tag: 'i',
1255                     cls: 'fa fas fa-' + this.fa,
1256                     html: this.html
1257                 };
1258             }
1259             
1260             var bw = this.badge_weight.length ? this.badge_weight :
1261                 (this.weight.length ? this.weight : 'secondary');
1262             bw = bw == 'default' ? 'secondary' : bw;
1263             
1264             cfg.cn = [
1265                 value,
1266                 {
1267                     tag: 'span',
1268                     cls: 'badge badge-' + bw,
1269                     html: this.badge
1270                 }
1271             ];
1272             
1273             cfg.html='';
1274         }
1275         
1276         if (this.menu) {
1277             cfg.cls += ' dropdown';
1278             cfg.html = typeof(cfg.html) != 'undefined' ?
1279                     cfg.html + ' <span class="caret"></span>' : '<span class="caret"></span>';
1280         }
1281         
1282         if (cfg.tag !== 'a' && this.href !== '') {
1283             throw "Tag must be a to set href.";
1284         } else if (this.href.length > 0) {
1285             cfg.href = this.href;
1286         }
1287         
1288         if(this.removeClass){
1289             cfg.cls = '';
1290         }
1291         
1292         if(this.target){
1293             cfg.target = this.target;
1294         }
1295         
1296         return cfg;
1297     },
1298     initEvents: function() {
1299        // Roo.log('init events?');
1300 //        Roo.log(this.el.dom);
1301         // add the menu...
1302         
1303         if (typeof (this.menu) != 'undefined') {
1304             this.menu.parentType = this.xtype;
1305             this.menu.triggerEl = this.el;
1306             this.addxtype(Roo.apply({}, this.menu));
1307         }
1308
1309
1310         if (this.el.hasClass('roo-button')) {
1311              this.el.on('click', this.onClick, this);
1312              this.el.on('dblclick', this.onDblClick, this);
1313         } else {
1314              this.el.select('.roo-button').on('click', this.onClick, this);
1315              this.el.select('.roo-button').on('dblclick', this.onDblClick, this);
1316              
1317         }
1318         // why?
1319         if(this.removeClass){
1320             this.el.on('click', this.onClick, this);
1321         }
1322         
1323         if (this.group === true) {
1324              if (this.pressed === false || this.pressed === true) {
1325                 // nothing
1326             } else {
1327                 this.pressed = false;
1328                 this.setActive(this.pressed);
1329             }
1330             
1331         }
1332         
1333         this.el.enableDisplayMode();
1334         
1335     },
1336     onClick : function(e)
1337     {
1338         if (this.disabled) {
1339             return;
1340         }
1341         
1342         Roo.log('button on click ');
1343         if(this.href === '' || this.preventDefault){
1344             e.preventDefault();
1345         }
1346         
1347         if (this.group) {
1348             if (this.pressed) {
1349                 // do nothing -
1350                 return;
1351             }
1352             this.setActive(true);
1353             var pi = this.parent().items;
1354             for (var i = 0;i < pi.length;i++) {
1355                 if (this == pi[i]) {
1356                     continue;
1357                 }
1358                 if (pi[i].el.hasClass('roo-button')) {
1359                     pi[i].setActive(false);
1360                 }
1361             }
1362             this.fireEvent('click', this, e);            
1363             return;
1364         }
1365         
1366         if (this.pressed === true || this.pressed === false) {
1367             this.toggleActive(e);
1368         }
1369         
1370         
1371         this.fireEvent('click', this, e);
1372     },
1373     onDblClick: function(e)
1374     {
1375         if (this.disabled) {
1376             return;
1377         }
1378         if(this.preventDefault){
1379             e.preventDefault();
1380         }
1381         this.fireEvent('dblclick', this, e);
1382     },
1383     /**
1384      * Enables this button
1385      */
1386     enable : function()
1387     {
1388         this.disabled = false;
1389         this.el.removeClass('disabled');
1390         this.el.dom.removeAttribute("disabled");
1391     },
1392     
1393     /**
1394      * Disable this button
1395      */
1396     disable : function()
1397     {
1398         this.disabled = true;
1399         this.el.addClass('disabled');
1400         this.el.attr("disabled", "disabled")
1401     },
1402      /**
1403      * sets the active state on/off, 
1404      * @param {Boolean} state (optional) Force a particular state
1405      */
1406     setActive : function(v) {
1407         
1408         this.el[v ? 'addClass' : 'removeClass']('active');
1409         this.pressed = v;
1410     },
1411      /**
1412      * toggles the current active state 
1413      */
1414     toggleActive : function(e)
1415     {
1416         this.setActive(!this.pressed); // this modifies pressed...
1417         this.fireEvent('toggle', this, e, this.pressed);
1418     },
1419      /**
1420      * get the current active state
1421      * @return {boolean} true if it's active
1422      */
1423     isActive : function()
1424     {
1425         return this.el.hasClass('active');
1426     },
1427     /**
1428      * set the text of the first selected button
1429      */
1430     setText : function(str)
1431     {
1432         this.el.select('.roo-button-text',true).first().dom.innerHTML = str;
1433     },
1434     /**
1435      * get the text of the first selected button
1436      */
1437     getText : function()
1438     {
1439         return this.el.select('.roo-button-text',true).first().dom.innerHTML;
1440     },
1441     
1442     setWeight : function(str)
1443     {
1444         this.el.removeClass(Roo.bootstrap.Button.weights.map(function(w) { return 'btn-' + w; } ) );
1445         this.el.removeClass(Roo.bootstrap.Button.weights.map(function(w) { return 'btn-outline-' + w; } ) );
1446         this.weight = str;
1447         var outline = this.outline ? 'outline-' : '';
1448         if (str == 'default') {
1449             this.el.addClass('btn-default btn-outline-secondary');        
1450             return;
1451         }
1452         this.el.addClass('btn-' + outline + str);        
1453     }
1454     
1455     
1456 });
1457 // fixme - this is probably generic bootstrap - should go in some kind of enum file.. - like sizes.
1458
1459 Roo.bootstrap.Button.weights = [
1460     'default',
1461     'secondary' ,
1462     'primary',
1463     'success',
1464     'info',
1465     'warning',
1466     'danger',
1467     'link',
1468     'light',
1469     'dark'              
1470    
1471 ];/*
1472  * - LGPL
1473  *
1474  * column
1475  * 
1476  */
1477
1478 /**
1479  * @class Roo.bootstrap.Column
1480  * @extends Roo.bootstrap.Component
1481  * @children Roo.bootstrap.Component
1482  * Bootstrap Column class
1483  * @cfg {Number} xs colspan out of 12 for mobile-sized screens or 0 for hidden
1484  * @cfg {Number} sm colspan out of 12 for tablet-sized screens or 0 for hidden
1485  * @cfg {Number} md colspan out of 12 for computer-sized screens or 0 for hidden
1486  * @cfg {Number} lg colspan out of 12 for large computer-sized screens or 0 for hidden
1487  * @cfg {Number} xsoff colspan offset out of 12 for mobile-sized screens or 0 for hidden
1488  * @cfg {Number} smoff colspan offset out of 12 for tablet-sized screens or 0 for hidden
1489  * @cfg {Number} mdoff colspan offset out of 12 for computer-sized screens or 0 for hidden
1490  * @cfg {Number} lgoff colspan offset out of 12 for large computer-sized screens or 0 for hidden
1491  *
1492  * 
1493  * @cfg {Boolean} hidden (true|false) hide the element
1494  * @cfg {String} alert (success|info|warning|danger) type alert (changes background / border...)
1495  * @cfg {String} fa (ban|check|...) font awesome icon
1496  * @cfg {Number} fasize (1|2|....) font awsome size
1497
1498  * @cfg {String} icon (info-sign|check|...) glyphicon name
1499
1500  * @cfg {String} html content of column.
1501  * 
1502  * @constructor
1503  * Create a new Column
1504  * @param {Object} config The config object
1505  */
1506
1507 Roo.bootstrap.Column = function(config){
1508     Roo.bootstrap.Column.superclass.constructor.call(this, config);
1509 };
1510
1511 Roo.extend(Roo.bootstrap.Column, Roo.bootstrap.Component,  {
1512     
1513     xs: false,
1514     sm: false,
1515     md: false,
1516     lg: false,
1517     xsoff: false,
1518     smoff: false,
1519     mdoff: false,
1520     lgoff: false,
1521     html: '',
1522     offset: 0,
1523     alert: false,
1524     fa: false,
1525     icon : false,
1526     hidden : false,
1527     fasize : 1,
1528     
1529     getAutoCreate : function(){
1530         var cfg = Roo.apply({}, Roo.bootstrap.Column.superclass.getAutoCreate.call(this));
1531         
1532         cfg = {
1533             tag: 'div',
1534             cls: 'column'
1535         };
1536         
1537         var settings=this;
1538         var sizes =   ['xs','sm','md','lg'];
1539         sizes.map(function(size ,ix){
1540             //Roo.log( size + ':' + settings[size]);
1541             
1542             if (settings[size+'off'] !== false) {
1543                 cfg.cls += ' col-' + size + '-offset-' + settings[size+'off'] ;
1544             }
1545             
1546             if (settings[size] === false) {
1547                 return;
1548             }
1549             
1550             if (!settings[size]) { // 0 = hidden
1551                 cfg.cls += ' hidden-' + size + ' hidden-' + size + '-down';
1552                 // bootsrap4
1553                 for (var i = ix; i > -1; i--) {
1554                     cfg.cls +=  ' d-' + sizes[i] + '-none'; 
1555                 }
1556                 
1557                 
1558                 return;
1559             }
1560             cfg.cls += ' col-' + size + '-' + settings[size] + (
1561                 size == 'xs' ? (' col-' + settings[size] ) : '' // bs4 col-{num} replaces col-xs
1562             );
1563             
1564         });
1565         
1566         if (this.hidden) {
1567             cfg.cls += ' hidden';
1568         }
1569         
1570         if (this.alert && ["success","info","warning", "danger"].indexOf(this.alert) > -1) {
1571             cfg.cls +=' alert alert-' + this.alert;
1572         }
1573         
1574         
1575         if (this.html.length) {
1576             cfg.html = this.html;
1577         }
1578         if (this.fa) {
1579             var fasize = '';
1580             if (this.fasize > 1) {
1581                 fasize = ' fa-' + this.fasize + 'x';
1582             }
1583             cfg.html = '<i class="fa fa-'+this.fa + fasize + '"></i>' + (cfg.html || '');
1584             
1585             
1586         }
1587         if (this.icon) {
1588             cfg.html = '<i class="glyphicon glyphicon-'+this.icon + '"></i>' +  (cfg.html || '');
1589         }
1590         
1591         return cfg;
1592     }
1593    
1594 });
1595
1596  
1597
1598  /*
1599  * - LGPL
1600  *
1601  * page container.
1602  * 
1603  */
1604
1605
1606 /**
1607  * @class Roo.bootstrap.Container
1608  * @extends Roo.bootstrap.Component
1609  * @children Roo.bootstrap.Component
1610  * @parent builder
1611  * Bootstrap Container class
1612  * @cfg {Boolean} jumbotron is it a jumbotron element
1613  * @cfg {String} html content of element
1614  * @cfg {String} well (lg|sm|md) a well, large, small or medium.
1615  * @cfg {String} panel (default|primary|success|info|warning|danger) render as panel  - type - primary/success.....
1616  * @cfg {String} header content of header (for panel)
1617  * @cfg {String} footer content of footer (for panel)
1618  * @cfg {String} sticky (footer|wrap|push) block to use as footer or body- needs css-bootstrap/sticky-footer.css
1619  * @cfg {String} tag (header|aside|section) type of HTML tag.
1620  * @cfg {String} alert (success|info|warning|danger) type alert (changes background / border...)
1621  * @cfg {String} fa font awesome icon
1622  * @cfg {String} icon (info-sign|check|...) glyphicon name
1623  * @cfg {Boolean} hidden (true|false) hide the element
1624  * @cfg {Boolean} expandable (true|false) default false
1625  * @cfg {Boolean} expanded (true|false) default true
1626  * @cfg {String} rheader contet on the right of header
1627  * @cfg {Boolean} clickable (true|false) default false
1628
1629  *     
1630  * @constructor
1631  * Create a new Container
1632  * @param {Object} config The config object
1633  */
1634
1635 Roo.bootstrap.Container = function(config){
1636     Roo.bootstrap.Container.superclass.constructor.call(this, config);
1637     
1638     this.addEvents({
1639         // raw events
1640          /**
1641          * @event expand
1642          * After the panel has been expand
1643          * 
1644          * @param {Roo.bootstrap.Container} this
1645          */
1646         "expand" : true,
1647         /**
1648          * @event collapse
1649          * After the panel has been collapsed
1650          * 
1651          * @param {Roo.bootstrap.Container} this
1652          */
1653         "collapse" : true,
1654         /**
1655          * @event click
1656          * When a element is chick
1657          * @param {Roo.bootstrap.Container} this
1658          * @param {Roo.EventObject} e
1659          */
1660         "click" : true
1661     });
1662 };
1663
1664 Roo.extend(Roo.bootstrap.Container, Roo.bootstrap.Component,  {
1665     
1666     jumbotron : false,
1667     well: '',
1668     panel : '',
1669     header: '',
1670     footer : '',
1671     sticky: '',
1672     tag : false,
1673     alert : false,
1674     fa: false,
1675     icon : false,
1676     expandable : false,
1677     rheader : '',
1678     expanded : true,
1679     clickable: false,
1680   
1681      
1682     getChildContainer : function() {
1683         
1684         if(!this.el){
1685             return false;
1686         }
1687         
1688         if (this.panel.length) {
1689             return this.el.select('.panel-body',true).first();
1690         }
1691         
1692         return this.el;
1693     },
1694     
1695     
1696     getAutoCreate : function(){
1697         
1698         var cfg = {
1699             tag : this.tag || 'div',
1700             html : '',
1701             cls : ''
1702         };
1703         if (this.jumbotron) {
1704             cfg.cls = 'jumbotron';
1705         }
1706         
1707         
1708         
1709         // - this is applied by the parent..
1710         //if (this.cls) {
1711         //    cfg.cls = this.cls + '';
1712         //}
1713         
1714         if (this.sticky.length) {
1715             
1716             var bd = Roo.get(document.body);
1717             if (!bd.hasClass('bootstrap-sticky')) {
1718                 bd.addClass('bootstrap-sticky');
1719                 Roo.select('html',true).setStyle('height', '100%');
1720             }
1721              
1722             cfg.cls += 'bootstrap-sticky-' + this.sticky;
1723         }
1724         
1725         
1726         if (this.well.length) {
1727             switch (this.well) {
1728                 case 'lg':
1729                 case 'sm':
1730                     cfg.cls +=' well well-' +this.well;
1731                     break;
1732                 default:
1733                     cfg.cls +=' well';
1734                     break;
1735             }
1736         }
1737         
1738         if (this.hidden) {
1739             cfg.cls += ' hidden';
1740         }
1741         
1742         
1743         if (this.alert && ["success","info","warning", "danger"].indexOf(this.alert) > -1) {
1744             cfg.cls +=' alert alert-' + this.alert;
1745         }
1746         
1747         var body = cfg;
1748         
1749         if (this.panel.length) {
1750             cfg.cls += ' panel panel-' + this.panel;
1751             cfg.cn = [];
1752             if (this.header.length) {
1753                 
1754                 var h = [];
1755                 
1756                 if(this.expandable){
1757                     
1758                     cfg.cls = cfg.cls + ' expandable';
1759                     
1760                     h.push({
1761                         tag: 'i',
1762                         cls: (this.expanded ? 'fa fa-minus' : 'fa fa-plus') 
1763                     });
1764                     
1765                 }
1766                 
1767                 h.push(
1768                     {
1769                         tag: 'span',
1770                         cls : 'panel-title',
1771                         html : (this.expandable ? '&nbsp;' : '') + this.header
1772                     },
1773                     {
1774                         tag: 'span',
1775                         cls: 'panel-header-right',
1776                         html: this.rheader
1777                     }
1778                 );
1779                 
1780                 cfg.cn.push({
1781                     cls : 'panel-heading',
1782                     style : this.expandable ? 'cursor: pointer' : '',
1783                     cn : h
1784                 });
1785                 
1786             }
1787             
1788             body = false;
1789             cfg.cn.push({
1790                 cls : 'panel-body' + (this.expanded ? '' : ' hide'),
1791                 html : this.html
1792             });
1793             
1794             
1795             if (this.footer.length) {
1796                 cfg.cn.push({
1797                     cls : 'panel-footer',
1798                     html : this.footer
1799                     
1800                 });
1801             }
1802             
1803         }
1804         
1805         if (body) {
1806             body.html = this.html || cfg.html;
1807             // prefix with the icons..
1808             if (this.fa) {
1809                 body.html = '<i class="fa fa-'+this.fa + '"></i>' + body.html ;
1810             }
1811             if (this.icon) {
1812                 body.html = '<i class="glyphicon glyphicon-'+this.icon + '"></i>' + body.html ;
1813             }
1814             
1815             
1816         }
1817         if ((!this.cls || !this.cls.length) && (!cfg.cls || !cfg.cls.length)) {
1818             cfg.cls =  'container';
1819         }
1820         
1821         return cfg;
1822     },
1823     
1824     initEvents: function() 
1825     {
1826         if(this.expandable){
1827             var headerEl = this.headerEl();
1828         
1829             if(headerEl){
1830                 headerEl.on('click', this.onToggleClick, this);
1831             }
1832         }
1833         
1834         if(this.clickable){
1835             this.el.on('click', this.onClick, this);
1836         }
1837         
1838     },
1839     
1840     onToggleClick : function()
1841     {
1842         var headerEl = this.headerEl();
1843         
1844         if(!headerEl){
1845             return;
1846         }
1847         
1848         if(this.expanded){
1849             this.collapse();
1850             return;
1851         }
1852         
1853         this.expand();
1854     },
1855     
1856     expand : function()
1857     {
1858         if(this.fireEvent('expand', this)) {
1859             
1860             this.expanded = true;
1861             
1862             //this.el.select('.panel-body',true).first().setVisibilityMode(Roo.Element.DISPLAY).show();
1863             
1864             this.el.select('.panel-body',true).first().removeClass('hide');
1865             
1866             var toggleEl = this.toggleEl();
1867
1868             if(!toggleEl){
1869                 return;
1870             }
1871
1872             toggleEl.removeClass(['fa-minus', 'fa-plus']).addClass(['fa-minus']);
1873         }
1874         
1875     },
1876     
1877     collapse : function()
1878     {
1879         if(this.fireEvent('collapse', this)) {
1880             
1881             this.expanded = false;
1882             
1883             //this.el.select('.panel-body',true).first().setVisibilityMode(Roo.Element.DISPLAY).hide();
1884             this.el.select('.panel-body',true).first().addClass('hide');
1885         
1886             var toggleEl = this.toggleEl();
1887
1888             if(!toggleEl){
1889                 return;
1890             }
1891
1892             toggleEl.removeClass(['fa-minus', 'fa-plus']).addClass(['fa-plus']);
1893         }
1894     },
1895     
1896     toggleEl : function()
1897     {
1898         if(!this.el || !this.panel.length || !this.header.length || !this.expandable){
1899             return;
1900         }
1901         
1902         return this.el.select('.panel-heading .fa',true).first();
1903     },
1904     
1905     headerEl : function()
1906     {
1907         if(!this.el || !this.panel.length || !this.header.length){
1908             return;
1909         }
1910         
1911         return this.el.select('.panel-heading',true).first()
1912     },
1913     
1914     bodyEl : function()
1915     {
1916         if(!this.el || !this.panel.length){
1917             return;
1918         }
1919         
1920         return this.el.select('.panel-body',true).first()
1921     },
1922     
1923     titleEl : function()
1924     {
1925         if(!this.el || !this.panel.length || !this.header.length){
1926             return;
1927         }
1928         
1929         return this.el.select('.panel-title',true).first();
1930     },
1931     
1932     setTitle : function(v)
1933     {
1934         var titleEl = this.titleEl();
1935         
1936         if(!titleEl){
1937             return;
1938         }
1939         
1940         titleEl.dom.innerHTML = v;
1941     },
1942     
1943     getTitle : function()
1944     {
1945         
1946         var titleEl = this.titleEl();
1947         
1948         if(!titleEl){
1949             return '';
1950         }
1951         
1952         return titleEl.dom.innerHTML;
1953     },
1954     
1955     setRightTitle : function(v)
1956     {
1957         var t = this.el.select('.panel-header-right',true).first();
1958         
1959         if(!t){
1960             return;
1961         }
1962         
1963         t.dom.innerHTML = v;
1964     },
1965     
1966     onClick : function(e)
1967     {
1968         e.preventDefault();
1969         
1970         this.fireEvent('click', this, e);
1971     }
1972 });
1973
1974  /**
1975  * @class Roo.bootstrap.Card
1976  * @extends Roo.bootstrap.Component
1977  * @children Roo.bootstrap.Component
1978  * @licence LGPL
1979  * Bootstrap Card class - note this has children as CardHeader/ImageTop/Footer.. - which should really be listed properties?
1980  *
1981  *
1982  * possible... may not be implemented..
1983  * @cfg {String} header_image  src url of image.
1984  * @cfg {String|Object} header
1985  * @cfg {Number} header_size (0|1|2|3|4|5) H1 or H2 etc.. 0 indicates default
1986  * @cfg {Number} header_weight  (primary|secondary|success|info|warning|danger|light|dark)
1987  * 
1988  * @cfg {String} title
1989  * @cfg {String} subtitle
1990  * @cfg {String|Boolean} html -- html contents - or just use children.. use false to hide it..
1991  * @cfg {String} footer
1992  
1993  * @cfg {String} weight (primary|warning|info|danger|secondary|success|light|dark)
1994  * 
1995  * @cfg {String} margin (0|1|2|3|4|5|auto)
1996  * @cfg {String} margin_top (0|1|2|3|4|5|auto)
1997  * @cfg {String} margin_bottom (0|1|2|3|4|5|auto)
1998  * @cfg {String} margin_left (0|1|2|3|4|5|auto)
1999  * @cfg {String} margin_right (0|1|2|3|4|5|auto)
2000  * @cfg {String} margin_x (0|1|2|3|4|5|auto)
2001  * @cfg {String} margin_y (0|1|2|3|4|5|auto)
2002  *
2003  * @cfg {String} padding (0|1|2|3|4|5)
2004  * @cfg {String} padding_top (0|1|2|3|4|5)next_to_card
2005  * @cfg {String} padding_bottom (0|1|2|3|4|5)
2006  * @cfg {String} padding_left (0|1|2|3|4|5)
2007  * @cfg {String} padding_right (0|1|2|3|4|5)
2008  * @cfg {String} padding_x (0|1|2|3|4|5)
2009  * @cfg {String} padding_y (0|1|2|3|4|5)
2010  *
2011  * @cfg {String} display (none|inline|inline-block|block|table|table-cell|table-row|flex|inline-flex)
2012  * @cfg {String} display_xs (none|inline|inline-block|block|table|table-cell|table-row|flex|inline-flex)
2013  * @cfg {String} display_sm (none|inline|inline-block|block|table|table-cell|table-row|flex|inline-flex)
2014  * @cfg {String} display_lg (none|inline|inline-block|block|table|table-cell|table-row|flex|inline-flex)
2015  * @cfg {String} display_xl (none|inline|inline-block|block|table|table-cell|table-row|flex|inline-flex)
2016  
2017  * @config {Boolean} dragable  if this card can be dragged.
2018  * @config {String} drag_group  group for drag
2019  * @config {Boolean} dropable  if this card can recieve other cards being dropped onto it..
2020  * @config {String} drop_group  group for drag
2021  * 
2022  * @config {Boolean} collapsable can the body be collapsed.
2023  * @config {Boolean} collapsed is the body collapsed when rendered...
2024  * @config {Boolean} rotateable can the body be rotated by clicking on it..
2025  * @config {Boolean} rotated is the body rotated when rendered...
2026  * 
2027  * @constructor
2028  * Create a new Container
2029  * @param {Object} config The config object
2030  */
2031
2032 Roo.bootstrap.Card = function(config){
2033     Roo.bootstrap.Card.superclass.constructor.call(this, config);
2034     
2035     this.addEvents({
2036          // raw events
2037         /**
2038          * @event drop
2039          * When a element a card is dropped
2040          * @param {Roo.bootstrap.Card} this
2041          *
2042          * 
2043          * @param {Roo.bootstrap.Card} move_card the card being dropped?
2044          * @param {String} position 'above' or 'below'
2045          * @param {Roo.bootstrap.Card} next_to_card What card position is relative to of 'false' for empty list.
2046         
2047          */
2048         'drop' : true,
2049          /**
2050          * @event rotate
2051          * When a element a card is rotate
2052          * @param {Roo.bootstrap.Card} this
2053          * @param {Roo.Element} n the node being dropped?
2054          * @param {Boolean} rotate status
2055          */
2056         'rotate' : true,
2057         /**
2058          * @event cardover
2059          * When a card element is dragged over ready to drop (return false to block dropable)
2060          * @param {Roo.bootstrap.Card} this
2061          * @param {Object} data from dragdrop 
2062          */
2063          'cardover' : true
2064          
2065     });
2066 };
2067
2068
2069 Roo.extend(Roo.bootstrap.Card, Roo.bootstrap.Component,  {
2070     
2071     
2072     weight : '',
2073     
2074     margin: '', /// may be better in component?
2075     margin_top: '', 
2076     margin_bottom: '', 
2077     margin_left: '',
2078     margin_right: '',
2079     margin_x: '',
2080     margin_y: '',
2081     
2082     padding : '',
2083     padding_top: '', 
2084     padding_bottom: '', 
2085     padding_left: '',
2086     padding_right: '',
2087     padding_x: '',
2088     padding_y: '',
2089     
2090     display: '', 
2091     display_xs: '', 
2092     display_sm: '', 
2093     display_lg: '',
2094     display_xl: '',
2095  
2096     header_image  : '',
2097     header : '',
2098     header_size : 0,
2099     title : '',
2100     subtitle : '',
2101     html : '',
2102     footer: '',
2103
2104     collapsable : false,
2105     collapsed : false,
2106     rotateable : false,
2107     rotated : false,
2108     
2109     dragable : false,
2110     drag_group : false,
2111     dropable : false,
2112     drop_group : false,
2113     childContainer : false,
2114     dropEl : false, /// the dom placeholde element that indicates drop location.
2115     containerEl: false, // body container
2116     bodyEl: false, // card-body
2117     headerContainerEl : false, //
2118     headerEl : false,
2119     header_imageEl : false,
2120     
2121     
2122     layoutCls : function()
2123     {
2124         var cls = '';
2125         var t = this;
2126         Roo.log(this.margin_bottom.length);
2127         ['', 'top', 'bottom', 'left', 'right', 'x', 'y' ].forEach(function(v) {
2128             // in theory these can do margin_top : ml-xs-3 ??? but we don't support that yet
2129             
2130             if (('' + t['margin' + (v.length ? '_' : '') + v]).length) {
2131                 cls += ' m' +  (v.length ? v[0]  : '') + '-' +  t['margin' + (v.length ? '_' : '') + v];
2132             }
2133             if (('' + t['padding' + (v.length ? '_' : '') + v]).length) {
2134                 cls += ' p' +  (v.length ? v[0]  : '') + '-' +  t['padding' + (v.length ? '_' : '') + v];
2135             }
2136         });
2137         
2138         ['', 'xs', 'sm', 'lg', 'xl'].forEach(function(v) {
2139             if (('' + t['display' + (v.length ? '_' : '') + v]).length) {
2140                 cls += ' d' +  (v.length ? '-' : '') + v + '-' + t['display' + (v.length ? '_' : '') + v]
2141             }
2142         });
2143         
2144         // more generic support?
2145         if (this.hidden) {
2146             cls += ' d-none';
2147         }
2148         
2149         return cls;
2150     },
2151  
2152        // Roo.log("Call onRender: " + this.xtype);
2153         /*  We are looking at something like this.
2154 <div class="card">
2155     <img src="..." class="card-img-top" alt="...">
2156     <div class="card-body">
2157         <h5 class="card-title">Card title</h5>
2158          <h6 class="card-subtitle mb-2 text-muted">Card subtitle</h6>
2159
2160         >> this bit is really the body...
2161         <div> << we will ad dthis in hopefully it will not break shit.
2162         
2163         ** card text does not actually have any styling...
2164         
2165             <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>
2166         
2167         </div> <<
2168           <a href="#" class="card-link">Card link</a>
2169           
2170     </div>
2171     <div class="card-footer">
2172         <small class="text-muted">Last updated 3 mins ago</small>
2173     </div>
2174 </div>
2175          */
2176     getAutoCreate : function(){
2177         
2178         var cfg = {
2179             tag : 'div',
2180             cls : 'card',
2181             cn : [ ]
2182         };
2183         
2184         if (this.weight.length && this.weight != 'light') {
2185             cfg.cls += ' text-white';
2186         } else {
2187             cfg.cls += ' text-dark'; // need as it's nested..
2188         }
2189         if (this.weight.length) {
2190             cfg.cls += ' bg-' + this.weight;
2191         }
2192         
2193         cfg.cls += ' ' + this.layoutCls(); 
2194         
2195         var hdr = false;
2196         var hdr_ctr = false;
2197         if (this.header.length) {
2198             hdr = {
2199                 tag : this.header_size > 0 ? 'h' + this.header_size : 'div',
2200                 cls : 'card-header ' + (this.header_weight ? 'bg-' + this.header_weight : ''),
2201                 cn : []
2202             };
2203             cfg.cn.push(hdr);
2204             hdr_ctr = hdr;
2205         } else {
2206             hdr = {
2207                 tag : 'div',
2208                 cls : 'card-header d-none ' + (this.header_weight ? 'bg-' + this.header_weight : ''),
2209                 cn : []
2210             };
2211             cfg.cn.push(hdr);
2212             hdr_ctr = hdr;
2213         }
2214         if (this.collapsable) {
2215             hdr_ctr = {
2216             tag : 'a',
2217             cls : 'd-block user-select-none',
2218             cn: [
2219                     {
2220                         tag: 'i',
2221                         cls : 'roo-collapse-toggle fa fa-chevron-down float-right ' + (this.collapsed ? 'collapsed' : '')
2222                     }
2223                    
2224                 ]
2225             };
2226             hdr.cn.push(hdr_ctr);
2227         }
2228         
2229         hdr_ctr.cn.push(        {
2230             tag: 'span',
2231             cls: 'roo-card-header-ctr' + ( this.header.length ? '' : ' d-none'),
2232             html : this.header
2233         });
2234         
2235         
2236         if (this.header_image.length) {
2237             cfg.cn.push({
2238                 tag : 'img',
2239                 cls : 'card-img-top',
2240                 src: this.header_image // escape?
2241             });
2242         } else {
2243             cfg.cn.push({
2244                     tag : 'div',
2245                     cls : 'card-img-top d-none' 
2246                 });
2247         }
2248             
2249         var body = {
2250             tag : 'div',
2251             cls : 'card-body' + (this.html === false  ? ' d-none' : ''),
2252             cn : []
2253         };
2254         var obody = body;
2255         if (this.collapsable || this.rotateable) {
2256             obody = {
2257                 tag: 'div',
2258                 cls : 'roo-collapsable collapse ' + (this.collapsed || this.rotated ? '' : 'show'),
2259                 cn : [  body ]
2260             };
2261         }
2262         
2263         cfg.cn.push(obody);
2264         
2265         if (this.title.length) {
2266             body.cn.push({
2267                 tag : 'div',
2268                 cls : 'card-title',
2269                 src: this.title // escape?
2270             });
2271         }  
2272         
2273         if (this.subtitle.length) {
2274             body.cn.push({
2275                 tag : 'div',
2276                 cls : 'card-title',
2277                 src: this.subtitle // escape?
2278             });
2279         }
2280         
2281         body.cn.push({
2282             tag : 'div',
2283             cls : 'roo-card-body-ctr'
2284         });
2285         
2286         if (this.html.length) {
2287             body.cn.push({
2288                 tag: 'div',
2289                 html : this.html
2290             });
2291         }
2292         // fixme ? handle objects?
2293         
2294         if (this.footer.length) {
2295            
2296             cfg.cn.push({
2297                 cls : 'card-footer ' + (this.rotated ? 'd-none' : ''),
2298                 html : this.footer
2299             });
2300             
2301         } else {
2302             cfg.cn.push({cls : 'card-footer d-none'});
2303         }
2304         
2305         // footer...
2306         
2307         return cfg;
2308     },
2309     
2310     
2311     getCardHeader : function()
2312     {
2313         var  ret = this.el.select('.card-header',true).first();
2314         if (ret.hasClass('d-none')) {
2315             ret.removeClass('d-none');
2316         }
2317         
2318         return ret;
2319     },
2320     getCardFooter : function()
2321     {
2322         var  ret = this.el.select('.card-footer',true).first();
2323         if (ret.hasClass('d-none')) {
2324             ret.removeClass('d-none');
2325         }
2326         
2327         return ret;
2328     },
2329     getCardImageTop : function()
2330     {
2331         var  ret = this.header_imageEl;
2332         if (ret.hasClass('d-none')) {
2333             ret.removeClass('d-none');
2334         }
2335             
2336         return ret;
2337     },
2338     
2339     getChildContainer : function()
2340     {
2341         
2342         if(!this.el){
2343             return false;
2344         }
2345         return this.el.select('.roo-card-body-ctr',true).first();    
2346     },
2347     
2348     initEvents: function() 
2349     {
2350         this.bodyEl = this.el.select('.card-body',true).first(); 
2351         this.containerEl = this.getChildContainer();
2352         if(this.dragable){
2353             this.dragZone = new Roo.dd.DragZone(this.getEl(), {
2354                     containerScroll: true,
2355                     ddGroup: this.drag_group || 'default_card_drag_group'
2356             });
2357             this.dragZone.getDragData = this.getDragData.createDelegate(this);
2358         }
2359         if (this.dropable) {
2360             this.dropZone = new Roo.dd.DropZone(this.el.select('.card-body',true).first() , {
2361                 containerScroll: true,
2362                 ddGroup: this.drop_group || 'default_card_drag_group'
2363             });
2364             this.dropZone.getTargetFromEvent = this.getTargetFromEvent.createDelegate(this);
2365             this.dropZone.onNodeEnter = this.onNodeEnter.createDelegate(this);
2366             this.dropZone.onNodeOver = this.onNodeOver.createDelegate(this);
2367             this.dropZone.onNodeOut = this.onNodeOut.createDelegate(this);
2368             this.dropZone.onNodeDrop = this.onNodeDrop.createDelegate(this);
2369         }
2370         
2371         if (this.collapsable) {
2372             this.el.select('.card-header',true).on('click', this.onToggleCollapse, this);
2373         }
2374         if (this.rotateable) {
2375             this.el.select('.card-header',true).on('click', this.onToggleRotate, this);
2376         }
2377         this.collapsableEl = this.el.select('.roo-collapsable',true).first();
2378          
2379         this.footerEl = this.el.select('.card-footer',true).first();
2380         this.collapsableToggleEl = this.el.select('.roo-collapse-toggle',true).first();
2381         this.headerContainerEl = this.el.select('.roo-card-header-ctr',true).first();
2382         this.headerEl = this.el.select('.card-header',true).first();
2383         
2384         if (this.rotated) {
2385             this.el.addClass('roo-card-rotated');
2386             this.fireEvent('rotate', this, true);
2387         }
2388         this.header_imageEl = this.el.select('.card-img-top',true).first(); 
2389         this.header_imageEl.on('load', this.onHeaderImageLoad, this );
2390         
2391     },
2392     getDragData : function(e)
2393     {
2394         var target = this.getEl();
2395         if (target) {
2396             //this.handleSelection(e);
2397             
2398             var dragData = {
2399                 source: this,
2400                 copy: false,
2401                 nodes: this.getEl(),
2402                 records: []
2403             };
2404             
2405             
2406             dragData.ddel = target.dom ;    // the div element
2407             Roo.log(target.getWidth( ));
2408             dragData.ddel.style.width = target.getWidth() + 'px';
2409             
2410             return dragData;
2411         }
2412         return false;
2413     },
2414     /**
2415     *    Part of the Roo.dd.DropZone interface. If no target node is found, the
2416     *    whole Element becomes the target, and this causes the drop gesture to append.
2417     *
2418     *    Returns an object:
2419     *     {
2420            
2421            position : 'below' or 'above'
2422            card  : relateive to card OBJECT (or true for no cards listed)
2423            items_n : relative to nth item in list
2424            card_n : relative to  nth card in list
2425     }
2426     *
2427     *    
2428     */
2429     getTargetFromEvent : function(e, dragged_card_el)
2430     {
2431         var target = e.getTarget();
2432         while ((target !== null) && (target.parentNode != this.containerEl.dom)) {
2433             target = target.parentNode;
2434         }
2435         
2436         var ret = {
2437             position: '',
2438             cards : [],
2439             card_n : -1,
2440             items_n : -1,
2441             card : false 
2442         };
2443         
2444         //Roo.log([ 'target' , target ? target.id : '--nothing--']);
2445         // see if target is one of the 'cards'...
2446         
2447         
2448         //Roo.log(this.items.length);
2449         var pos = false;
2450         
2451         var last_card_n = 0;
2452         var cards_len  = 0;
2453         for (var i = 0;i< this.items.length;i++) {
2454             
2455             if (!this.items[i].el.hasClass('card')) {
2456                  continue;
2457             }
2458             pos = this.getDropPoint(e, this.items[i].el.dom);
2459             
2460             cards_len = ret.cards.length;
2461             //Roo.log(this.items[i].el.dom.id);
2462             ret.cards.push(this.items[i]);
2463             last_card_n  = i;
2464             if (ret.card_n < 0 && pos == 'above') {
2465                 ret.position = cards_len > 0 ? 'below' : pos;
2466                 ret.items_n = i > 0 ? i - 1 : 0;
2467                 ret.card_n  = cards_len  > 0 ? cards_len - 1 : 0;
2468                 ret.card = ret.cards[ret.card_n];
2469             }
2470         }
2471         if (!ret.cards.length) {
2472             ret.card = true;
2473             ret.position = 'below';
2474             ret.items_n;
2475             return ret;
2476         }
2477         // could not find a card.. stick it at the end..
2478         if (ret.card_n < 0) {
2479             ret.card_n = last_card_n;
2480             ret.card = ret.cards[last_card_n];
2481             ret.items_n = this.items.indexOf(ret.cards[last_card_n]);
2482             ret.position = 'below';
2483         }
2484         
2485         if (this.items[ret.items_n].el == dragged_card_el) {
2486             return false;
2487         }
2488         
2489         if (ret.position == 'below') {
2490             var card_after = ret.card_n+1 == ret.cards.length ? false : ret.cards[ret.card_n+1];
2491             
2492             if (card_after  && card_after.el == dragged_card_el) {
2493                 return false;
2494             }
2495             return ret;
2496         }
2497         
2498         // its's after ..
2499         var card_before = ret.card_n > 0 ? ret.cards[ret.card_n-1] : false;
2500         
2501         if (card_before  && card_before.el == dragged_card_el) {
2502             return false;
2503         }
2504         
2505         return ret;
2506     },
2507     
2508     onNodeEnter : function(n, dd, e, data){
2509         return false;
2510     },
2511     onNodeOver : function(n, dd, e, data)
2512     {
2513        
2514         var target_info = this.getTargetFromEvent(e,data.source.el);
2515         if (target_info === false) {
2516             this.dropPlaceHolder('hide');
2517             return false;
2518         }
2519         Roo.log(['getTargetFromEvent', target_info ]);
2520         
2521         
2522         if (this.fireEvent('cardover', this, [ data ]) === false) {
2523             return false;
2524         }
2525         
2526         this.dropPlaceHolder('show', target_info,data);
2527         
2528         return false; 
2529     },
2530     onNodeOut : function(n, dd, e, data){
2531         this.dropPlaceHolder('hide');
2532      
2533     },
2534     onNodeDrop : function(n, dd, e, data)
2535     {
2536         
2537         // call drop - return false if
2538         
2539         // this could actually fail - if the Network drops..
2540         // we will ignore this at present..- client should probably reload
2541         // the whole set of cards if stuff like that fails.
2542         
2543         
2544         var info = this.getTargetFromEvent(e,data.source.el);
2545         if (info === false) {
2546             return false;
2547         }
2548         this.dropPlaceHolder('hide');
2549   
2550           
2551     
2552         this.acceptCard(data.source, info.position, info.card, info.items_n);
2553         return true;
2554          
2555     },
2556     firstChildCard : function()
2557     {
2558         for (var i = 0;i< this.items.length;i++) {
2559             
2560             if (!this.items[i].el.hasClass('card')) {
2561                  continue;
2562             }
2563             return this.items[i];
2564         }
2565         return this.items.length ? this.items[this.items.length-1] : false; // don't try and put stuff after the cards...
2566     },
2567     /**
2568      * accept card
2569      *
2570      * -        card.acceptCard(move_card, info.position, info.card, info.items_n);
2571      */
2572     acceptCard : function(move_card,  position, next_to_card )
2573     {
2574         if (this.fireEvent("drop", this, move_card, position, next_to_card) === false) {
2575             return false;
2576         }
2577         
2578         var to_items_n = next_to_card ? this.items.indexOf(next_to_card) : 0;
2579         
2580         move_card.parent().removeCard(move_card);
2581         
2582         
2583         var dom = move_card.el.dom;
2584         dom.style.width = ''; // clear with - which is set by drag.
2585         
2586         if (next_to_card !== false && next_to_card !== true && next_to_card.el.dom.parentNode) {
2587             var cardel = next_to_card.el.dom;
2588             
2589             if (position == 'above' ) {
2590                 cardel.parentNode.insertBefore(dom, cardel);
2591             } else if (cardel.nextSibling) {
2592                 cardel.parentNode.insertBefore(dom,cardel.nextSibling);
2593             } else {
2594                 cardel.parentNode.append(dom);
2595             }
2596         } else {
2597             // card container???
2598             this.containerEl.dom.append(dom);
2599         }
2600         
2601         //FIXME HANDLE card = true 
2602         
2603         // add this to the correct place in items.
2604         
2605         // remove Card from items.
2606         
2607        
2608         if (this.items.length) {
2609             var nitems = [];
2610             //Roo.log([info.items_n, info.position, this.items.length]);
2611             for (var i =0; i < this.items.length; i++) {
2612                 if (i == to_items_n && position == 'above') {
2613                     nitems.push(move_card);
2614                 }
2615                 nitems.push(this.items[i]);
2616                 if (i == to_items_n && position == 'below') {
2617                     nitems.push(move_card);
2618                 }
2619             }
2620             this.items = nitems;
2621             Roo.log(this.items);
2622         } else {
2623             this.items.push(move_card);
2624         }
2625         
2626         move_card.parentId = this.id;
2627         
2628         return true;
2629         
2630         
2631     },
2632     removeCard : function(c)
2633     {
2634         this.items = this.items.filter(function(e) { return e != c });
2635  
2636         var dom = c.el.dom;
2637         dom.parentNode.removeChild(dom);
2638         dom.style.width = ''; // clear with - which is set by drag.
2639         c.parentId = false;
2640         
2641     },
2642     
2643     /**    Decide whether to drop above or below a View node. */
2644     getDropPoint : function(e, n, dd)
2645     {
2646         if (dd) {
2647              return false;
2648         }
2649         if (n == this.containerEl.dom) {
2650             return "above";
2651         }
2652         var t = Roo.lib.Dom.getY(n), b = t + n.offsetHeight;
2653         var c = t + (b - t) / 2;
2654         var y = Roo.lib.Event.getPageY(e);
2655         if(y <= c) {
2656             return "above";
2657         }else{
2658             return "below";
2659         }
2660     },
2661     onToggleCollapse : function(e)
2662         {
2663         if (this.collapsed) {
2664             this.el.select('.roo-collapse-toggle').removeClass('collapsed');
2665             this.collapsableEl.addClass('show');
2666             this.collapsed = false;
2667             return;
2668         }
2669         this.el.select('.roo-collapse-toggle').addClass('collapsed');
2670         this.collapsableEl.removeClass('show');
2671         this.collapsed = true;
2672         
2673     
2674     },
2675     
2676     onToggleRotate : function(e)
2677     {
2678         this.collapsableEl.removeClass('show');
2679         this.footerEl.removeClass('d-none');
2680         this.el.removeClass('roo-card-rotated');
2681         this.el.removeClass('d-none');
2682         if (this.rotated) {
2683             
2684             this.collapsableEl.addClass('show');
2685             this.rotated = false;
2686             this.fireEvent('rotate', this, this.rotated);
2687             return;
2688         }
2689         this.el.addClass('roo-card-rotated');
2690         this.footerEl.addClass('d-none');
2691         this.el.select('.roo-collapsable').removeClass('show');
2692         
2693         this.rotated = true;
2694         this.fireEvent('rotate', this, this.rotated);
2695     
2696     },
2697     
2698     dropPlaceHolder: function (action, info, data)
2699     {
2700         if (this.dropEl === false) {
2701             this.dropEl = Roo.DomHelper.append(this.containerEl, {
2702             cls : 'd-none'
2703             },true);
2704         }
2705         this.dropEl.removeClass(['d-none', 'd-block']);        
2706         if (action == 'hide') {
2707             
2708             this.dropEl.addClass('d-none');
2709             return;
2710         }
2711         // FIXME - info.card == true!!!
2712         this.dropEl.dom.parentNode.removeChild(this.dropEl.dom);
2713         
2714         if (info.card !== true) {
2715             var cardel = info.card.el.dom;
2716             
2717             if (info.position == 'above') {
2718                 cardel.parentNode.insertBefore(this.dropEl.dom, cardel);
2719             } else if (cardel.nextSibling) {
2720                 cardel.parentNode.insertBefore(this.dropEl.dom,cardel.nextSibling);
2721             } else {
2722                 cardel.parentNode.append(this.dropEl.dom);
2723             }
2724         } else {
2725             // card container???
2726             this.containerEl.dom.append(this.dropEl.dom);
2727         }
2728         
2729         this.dropEl.addClass('d-block roo-card-dropzone');
2730         
2731         this.dropEl.setHeight( Roo.get(data.ddel).getHeight() );
2732         
2733         
2734     
2735     
2736     
2737     },
2738     setHeaderText: function(html)
2739     {
2740         this.header = html;
2741         if (this.headerContainerEl) {
2742             this.headerContainerEl.dom.innerHTML = html;
2743         }
2744     },
2745     onHeaderImageLoad : function(ev, he)
2746     {
2747         if (!this.header_image_fit_square) {
2748             return;
2749         }
2750         
2751         var hw = he.naturalHeight / he.naturalWidth;
2752         // wide image = < 0
2753         // tall image = > 1
2754         //var w = he.dom.naturalWidth;
2755         var ww = he.width;
2756         he.style.left =  0;
2757         he.style.position =  'relative';
2758         if (hw > 1) {
2759             var nw = (ww * (1/hw));
2760             Roo.get(he).setSize( ww * (1/hw),  ww);
2761             he.style.left =  ((ww - nw)/ 2) + 'px';
2762             he.style.position =  'relative';
2763         }
2764
2765     }
2766
2767     
2768 });
2769
2770 /*
2771  * - LGPL
2772  *
2773  * Card header - holder for the card header elements.
2774  * 
2775  */
2776
2777 /**
2778  * @class Roo.bootstrap.CardHeader
2779  * @extends Roo.bootstrap.Element
2780  * @parent Roo.bootstrap.Card
2781  * @children Roo.bootstrap.Component
2782  * Bootstrap CardHeader class
2783  * @constructor
2784  * Create a new Card Header - that you can embed children into
2785  * @param {Object} config The config object
2786  */
2787
2788 Roo.bootstrap.CardHeader = function(config){
2789     Roo.bootstrap.CardHeader.superclass.constructor.call(this, config);
2790 };
2791
2792 Roo.extend(Roo.bootstrap.CardHeader, Roo.bootstrap.Element,  {
2793     
2794     
2795     container_method : 'getCardHeader' 
2796     
2797      
2798     
2799     
2800    
2801 });
2802
2803  
2804
2805  /*
2806  * - LGPL
2807  *
2808  * Card footer - holder for the card footer elements.
2809  * 
2810  */
2811
2812 /**
2813  * @class Roo.bootstrap.CardFooter
2814  * @extends Roo.bootstrap.Element
2815  * @parent Roo.bootstrap.Card
2816  * @children Roo.bootstrap.Component
2817  * Bootstrap CardFooter class
2818  * 
2819  * @constructor
2820  * Create a new Card Footer - that you can embed children into
2821  * @param {Object} config The config object
2822  */
2823
2824 Roo.bootstrap.CardFooter = function(config){
2825     Roo.bootstrap.CardFooter.superclass.constructor.call(this, config);
2826 };
2827
2828 Roo.extend(Roo.bootstrap.CardFooter, Roo.bootstrap.Element,  {
2829     
2830     
2831     container_method : 'getCardFooter' 
2832     
2833      
2834     
2835     
2836    
2837 });
2838
2839  
2840
2841  /*
2842  * - LGPL
2843  *
2844  * Card header - holder for the card header elements.
2845  * 
2846  */
2847
2848 /**
2849  * @class Roo.bootstrap.CardImageTop
2850  * @extends Roo.bootstrap.Element
2851  * @parent Roo.bootstrap.Card
2852  * @children Roo.bootstrap.Component
2853  * Bootstrap CardImageTop class
2854  * 
2855  * @constructor
2856  * Create a new Card Image Top container
2857  * @param {Object} config The config object
2858  */
2859
2860 Roo.bootstrap.CardImageTop = function(config){
2861     Roo.bootstrap.CardImageTop.superclass.constructor.call(this, config);
2862 };
2863
2864 Roo.extend(Roo.bootstrap.CardImageTop, Roo.bootstrap.Element,  {
2865     
2866    
2867     container_method : 'getCardImageTop' 
2868     
2869      
2870     
2871    
2872 });
2873
2874  
2875
2876  
2877 /*
2878 * Licence: LGPL
2879 */
2880
2881 /**
2882  * @class Roo.bootstrap.ButtonUploader
2883  * @extends Roo.bootstrap.Button
2884  * Bootstrap Button Uploader class - it's a button which when you add files to it
2885  *
2886  * 
2887  * @cfg {Number} errorTimeout default 3000
2888  * @cfg {Array}  images  an array of ?? Img objects ??? when loading existing files..
2889  * @cfg {Array}  html The button text.
2890  * @cfg {Boolean}  multiple (default true) Should the upload allow multiple files to be uploaded.
2891  *
2892  * @constructor
2893  * Create a new CardUploader
2894  * @param {Object} config The config object
2895  */
2896
2897 Roo.bootstrap.ButtonUploader = function(config){
2898     
2899  
2900     
2901     Roo.bootstrap.ButtonUploader.superclass.constructor.call(this, config);
2902     
2903      
2904      this.addEvents({
2905          // raw events
2906         /**
2907          * @event beforeselect
2908          * When button is pressed, before show upload files dialog is shown
2909          * @param {Roo.bootstrap.UploaderButton} this
2910          *
2911          */
2912         'beforeselect' : true,
2913          /**
2914          * @event fired when files have been selected, 
2915          * When a the download link is clicked
2916          * @param {Roo.bootstrap.UploaderButton} this
2917          * @param {Array} Array of files that have been uploaded
2918          */
2919         'uploaded' : true
2920         
2921     });
2922 };
2923  
2924 Roo.extend(Roo.bootstrap.ButtonUploader, Roo.bootstrap.Button,  {
2925     
2926      
2927     errorTimeout : 3000,
2928      
2929     images : false,
2930    
2931     fileCollection : false,
2932     allowBlank : true,
2933     
2934     multiple : true,
2935     
2936     getAutoCreate : function()
2937     {
2938        
2939         
2940         return  {
2941             cls :'div' ,
2942             cn : [
2943                 Roo.bootstrap.Button.prototype.getAutoCreate.call(this) 
2944             ]
2945         };
2946            
2947          
2948     },
2949      
2950    
2951     initEvents : function()
2952     {
2953         
2954         Roo.bootstrap.Button.prototype.initEvents.call(this);
2955         
2956         
2957         
2958         
2959         
2960         this.urlAPI = (window.createObjectURL && window) || 
2961                                 (window.URL && URL.revokeObjectURL && URL) || 
2962                                 (window.webkitURL && webkitURL);
2963                         
2964         var im = {
2965             tag: 'input',
2966             type : 'file',
2967             cls : 'd-none  roo-card-upload-selector' 
2968           
2969         };
2970         if (this.multiple) {
2971             im.multiple = 'multiple';
2972         }
2973         this.selectorEl = Roo.get(document.body).createChild(im); // so it does not capture click event for navitem.
2974        
2975         //this.selectorEl = this.el.select('.roo-card-upload-selector', true).first();
2976         
2977         this.selectorEl.on('change', this.onFileSelected, this);
2978          
2979          
2980        
2981     },
2982     
2983    
2984     onClick : function(e)
2985     {
2986         e.preventDefault();
2987         
2988         if ( this.fireEvent('beforeselect', this) === false) {
2989             return;
2990         }
2991          
2992         this.selectorEl.dom.click();
2993          
2994     },
2995     
2996     onFileSelected : function(e)
2997     {
2998         e.preventDefault();
2999         
3000         if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
3001             return;
3002         }
3003         var files = Array.prototype.slice.call(this.selectorEl.dom.files);
3004         this.selectorEl.dom.value  = '';// hopefully reset..
3005         
3006         this.fireEvent('uploaded', this,  files );
3007         
3008     },
3009     
3010        
3011    
3012     
3013     /**
3014      * addCard - add an Attachment to the uploader
3015      * @param data - the data about the image to upload
3016      *
3017      * {
3018           id : 123
3019           title : "Title of file",
3020           is_uploaded : false,
3021           src : "http://.....",
3022           srcfile : { the File upload object },
3023           mimetype : file.type,
3024           preview : false,
3025           is_deleted : 0
3026           .. any other data...
3027         }
3028      *
3029      * 
3030     */
3031      
3032     reset: function()
3033     {
3034          
3035          this.selectorEl
3036     } 
3037     
3038     
3039     
3040     
3041 });
3042  /*
3043  * - LGPL
3044  *
3045  * image
3046  * 
3047  */
3048
3049
3050 /**
3051  * @class Roo.bootstrap.Img
3052  * @extends Roo.bootstrap.Component
3053  * Bootstrap Img class
3054  * @cfg {Boolean} imgResponsive false | true
3055  * @cfg {String} border rounded | circle | thumbnail
3056  * @cfg {String} src image source
3057  * @cfg {String} alt image alternative text
3058  * @cfg {String} href a tag href
3059  * @cfg {String} target (_self|_blank|_parent|_top)target for a href.
3060  * @cfg {String} xsUrl xs image source
3061  * @cfg {String} smUrl sm image source
3062  * @cfg {String} mdUrl md image source
3063  * @cfg {String} lgUrl lg image source
3064  * @cfg {Boolean} backgroundContain (use style background and contain image in content)
3065  * 
3066  * @constructor
3067  * Create a new Input
3068  * @param {Object} config The config object
3069  */
3070
3071 Roo.bootstrap.Img = function(config){
3072     Roo.bootstrap.Img.superclass.constructor.call(this, config);
3073     
3074     this.addEvents({
3075         // img events
3076         /**
3077          * @event click
3078          * The img click event for the img.
3079          * @param {Roo.EventObject} e
3080          */
3081         "click" : true,
3082         /**
3083          * @event load
3084          * The when any image loads
3085          * @param {Roo.EventObject} e
3086          */
3087         "load" : true
3088     });
3089 };
3090
3091 Roo.extend(Roo.bootstrap.Img, Roo.bootstrap.Component,  {
3092     
3093     imgResponsive: true,
3094     border: '',
3095     src: 'about:blank',
3096     href: false,
3097     target: false,
3098     xsUrl: '',
3099     smUrl: '',
3100     mdUrl: '',
3101     lgUrl: '',
3102     backgroundContain : false,
3103
3104     getAutoCreate : function()
3105     {   
3106         if(this.src || (!this.xsUrl && !this.smUrl && !this.mdUrl && !this.lgUrl)){
3107             return this.createSingleImg();
3108         }
3109         
3110         var cfg = {
3111             tag: 'div',
3112             cls: 'roo-image-responsive-group',
3113             cn: []
3114         };
3115         var _this = this;
3116         
3117         Roo.each(['xs', 'sm', 'md', 'lg'], function(size){
3118             
3119             if(!_this[size + 'Url']){
3120                 return;
3121             }
3122             
3123             var img = {
3124                 tag: 'img',
3125                 cls: (_this.imgResponsive) ? 'img-responsive' : '',
3126                 html: _this.html || cfg.html,
3127                 src: _this[size + 'Url']
3128             };
3129             
3130             img.cls += ' roo-image-responsive-' + size;
3131             
3132             var s = ['xs', 'sm', 'md', 'lg'];
3133             
3134             s.splice(s.indexOf(size), 1);
3135             
3136             Roo.each(s, function(ss){
3137                 img.cls += ' hidden-' + ss;
3138             });
3139             
3140             if (['rounded','circle','thumbnail'].indexOf(_this.border)>-1) {
3141                 cfg.cls += ' img-' + _this.border;
3142             }
3143             
3144             if(_this.alt){
3145                 cfg.alt = _this.alt;
3146             }
3147             
3148             if(_this.href){
3149                 var a = {
3150                     tag: 'a',
3151                     href: _this.href,
3152                     cn: [
3153                         img
3154                     ]
3155                 };
3156
3157                 if(this.target){
3158                     a.target = _this.target;
3159                 }
3160             }
3161             
3162             cfg.cn.push((_this.href) ? a : img);
3163             
3164         });
3165         
3166         return cfg;
3167     },
3168     
3169     createSingleImg : function()
3170     {
3171         var cfg = {
3172             tag: 'img',
3173             cls: (this.imgResponsive) ? 'img-responsive' : '',
3174             html : null,
3175             src : Roo.BLANK_IMAGE_URL  // just incase src get's set to undefined?!?
3176         };
3177         
3178         if (this.backgroundContain) {
3179             cfg.cls += ' background-contain';
3180         }
3181         
3182         cfg.html = this.html || cfg.html;
3183         
3184         if (this.backgroundContain) {
3185             cfg.style="background-image: url(" + this.src + ')';
3186         } else {
3187             cfg.src = this.src || cfg.src;
3188         }
3189         
3190         if (['rounded','circle','thumbnail'].indexOf(this.border)>-1) {
3191             cfg.cls += ' img-' + this.border;
3192         }
3193         
3194         if(this.alt){
3195             cfg.alt = this.alt;
3196         }
3197         
3198         if(this.href){
3199             var a = {
3200                 tag: 'a',
3201                 href: this.href,
3202                 cn: [
3203                     cfg
3204                 ]
3205             };
3206             
3207             if(this.target){
3208                 a.target = this.target;
3209             }
3210             
3211         }
3212         
3213         return (this.href) ? a : cfg;
3214     },
3215     
3216     initEvents: function() 
3217     {
3218         if(!this.href){
3219             this.el.on('click', this.onClick, this);
3220         }
3221         if(this.src || (!this.xsUrl && !this.smUrl && !this.mdUrl && !this.lgUrl)){
3222             this.el.on('load', this.onImageLoad, this);
3223         } else {
3224             // not sure if this works.. not tested
3225             this.el.select('img', true).on('load', this.onImageLoad, this);
3226         }
3227         
3228     },
3229     
3230     onClick : function(e)
3231     {
3232         Roo.log('img onclick');
3233         this.fireEvent('click', this, e);
3234     },
3235     onImageLoad: function(e)
3236     {
3237         Roo.log('img load');
3238         this.fireEvent('load', this, e);
3239     },
3240     
3241     /**
3242      * Sets the url of the image - used to update it
3243      * @param {String} url the url of the image
3244      */
3245     
3246     setSrc : function(url)
3247     {
3248         this.src =  url;
3249         
3250         if(this.src || (!this.xsUrl && !this.smUrl && !this.mdUrl && !this.lgUrl)){
3251             if (this.backgroundContain) {
3252                 this.el.dom.style.backgroundImage =  'url(' + url + ')';
3253             } else {
3254                 this.el.dom.src =  url;
3255             }
3256             return;
3257         }
3258         
3259         this.el.select('img', true).first().dom.src =  url;
3260     }
3261     
3262     
3263    
3264 });
3265
3266  /*
3267  * - LGPL
3268  *
3269  * image
3270  * 
3271  */
3272
3273
3274 /**
3275  * @class Roo.bootstrap.Link
3276  * @extends Roo.bootstrap.Component
3277  * @children Roo.bootstrap.Component
3278  * Bootstrap Link Class (eg. '<a href>')
3279  
3280  * @cfg {String} alt image alternative text
3281  * @cfg {String} href a tag href
3282  * @cfg {String} target (_self|_blank|_parent|_top) target for a href.
3283  * @cfg {String} html the content of the link.
3284  * @cfg {String} anchor name for the anchor link
3285  * @cfg {String} fa - favicon
3286
3287  * @cfg {Boolean} preventDefault (true | false) default false
3288
3289  * 
3290  * @constructor
3291  * Create a new Input
3292  * @param {Object} config The config object
3293  */
3294
3295 Roo.bootstrap.Link = function(config){
3296     Roo.bootstrap.Link.superclass.constructor.call(this, config);
3297     
3298     this.addEvents({
3299         // img events
3300         /**
3301          * @event click
3302          * The img click event for the img.
3303          * @param {Roo.EventObject} e
3304          */
3305         "click" : true
3306     });
3307 };
3308
3309 Roo.extend(Roo.bootstrap.Link, Roo.bootstrap.Component,  {
3310     
3311     href: false,
3312     target: false,
3313     preventDefault: false,
3314     anchor : false,
3315     alt : false,
3316     fa: false,
3317
3318
3319     getAutoCreate : function()
3320     {
3321         var html = this.html || '';
3322         
3323         if (this.fa !== false) {
3324             html = '<i class="fa fa-' + this.fa + '"></i>';
3325         }
3326         var cfg = {
3327             tag: 'a'
3328         };
3329         // anchor's do not require html/href...
3330         if (this.anchor === false) {
3331             cfg.html = html;
3332             cfg.href = this.href || '#';
3333         } else {
3334             cfg.name = this.anchor;
3335             if (this.html !== false || this.fa !== false) {
3336                 cfg.html = html;
3337             }
3338             if (this.href !== false) {
3339                 cfg.href = this.href;
3340             }
3341         }
3342         
3343         if(this.alt !== false){
3344             cfg.alt = this.alt;
3345         }
3346         
3347         
3348         if(this.target !== false) {
3349             cfg.target = this.target;
3350         }
3351         
3352         return cfg;
3353     },
3354     
3355     initEvents: function() {
3356         
3357         if(!this.href || this.preventDefault){
3358             this.el.on('click', this.onClick, this);
3359         }
3360     },
3361     
3362     onClick : function(e)
3363     {
3364         if(this.preventDefault){
3365             e.preventDefault();
3366         }
3367         //Roo.log('img onclick');
3368         this.fireEvent('click', this, e);
3369     }
3370    
3371 });
3372
3373  /*
3374  * - LGPL
3375  *
3376  * header
3377  * 
3378  */
3379
3380 /**
3381  * @class Roo.bootstrap.Header
3382  * @extends Roo.bootstrap.Component
3383  * @children Roo.bootstrap.Component
3384  * Bootstrap Header class
3385  *
3386  * 
3387  * @cfg {String} html content of header
3388  * @cfg {Number} level (1|2|3|4|5|6) default 1
3389  * 
3390  * @constructor
3391  * Create a new Header
3392  * @param {Object} config The config object
3393  */
3394
3395
3396 Roo.bootstrap.Header  = function(config){
3397     Roo.bootstrap.Header.superclass.constructor.call(this, config);
3398 };
3399
3400 Roo.extend(Roo.bootstrap.Header, Roo.bootstrap.Component,  {
3401     
3402     //href : false,
3403     html : false,
3404     level : 1,
3405     
3406     
3407     
3408     getAutoCreate : function(){
3409         
3410         
3411         
3412         var cfg = {
3413             tag: 'h' + (1 *this.level),
3414             html: this.html || ''
3415         } ;
3416         
3417         return cfg;
3418     }
3419    
3420 });
3421
3422  
3423
3424  /**
3425  * @class Roo.bootstrap.MenuMgr
3426  * @licence LGPL
3427  * Provides a common registry of all menu items on a page so that they can be easily accessed by id.
3428  * @static
3429  */
3430 Roo.bootstrap.menu.Manager = function(){
3431    var menus, active, groups = {}, attached = false, lastShow = new Date();
3432
3433    // private - called when first menu is created
3434    function init(){
3435        menus = {};
3436        active = new Roo.util.MixedCollection();
3437        Roo.get(document).addKeyListener(27, function(){
3438            if(active.length > 0){
3439                hideAll();
3440            }
3441        });
3442    }
3443
3444    // private
3445    function hideAll(){
3446        if(active && active.length > 0){
3447            var c = active.clone();
3448            c.each(function(m){
3449                m.hide();
3450            });
3451        }
3452    }
3453
3454    // private
3455    function onHide(m){
3456        active.remove(m);
3457        if(active.length < 1){
3458            Roo.get(document).un("mouseup", onMouseDown);
3459             
3460            attached = false;
3461        }
3462    }
3463
3464    // private
3465    function onShow(m){
3466        var last = active.last();
3467        lastShow = new Date();
3468        active.add(m);
3469        if(!attached){
3470           Roo.get(document).on("mouseup", onMouseDown);
3471            
3472            attached = true;
3473        }
3474        if(m.parentMenu){
3475           //m.getEl().setZIndex(parseInt(m.parentMenu.getEl().getStyle("z-index"), 10) + 3);
3476           m.parentMenu.activeChild = m;
3477        }else if(last && last.isVisible()){
3478           //m.getEl().setZIndex(parseInt(last.getEl().getStyle("z-index"), 10) + 3);
3479        }
3480    }
3481
3482    // private
3483    function onBeforeHide(m){
3484        if(m.activeChild){
3485            m.activeChild.hide();
3486        }
3487        if(m.autoHideTimer){
3488            clearTimeout(m.autoHideTimer);
3489            delete m.autoHideTimer;
3490        }
3491    }
3492
3493    // private
3494    function onBeforeShow(m){
3495        var pm = m.parentMenu;
3496        if(!pm && !m.allowOtherMenus){
3497            hideAll();
3498        }else if(pm && pm.activeChild && active != m){
3499            pm.activeChild.hide();
3500        }
3501    }
3502
3503    // private this should really trigger on mouseup..
3504    function onMouseDown(e){
3505         Roo.log("on Mouse Up");
3506         
3507         if(lastShow.getElapsed() > 50 && active.length > 0 && !e.getTarget(".dropdown-menu") && !e.getTarget('.user-menu')){
3508             Roo.log("MenuManager hideAll");
3509             hideAll();
3510             e.stopEvent();
3511         }
3512         
3513         
3514    }
3515
3516    // private
3517    function onBeforeCheck(mi, state){
3518        if(state){
3519            var g = groups[mi.group];
3520            for(var i = 0, l = g.length; i < l; i++){
3521                if(g[i] != mi){
3522                    g[i].setChecked(false);
3523                }
3524            }
3525        }
3526    }
3527
3528    return {
3529
3530        /**
3531         * Hides all menus that are currently visible
3532         */
3533        hideAll : function(){
3534             hideAll();  
3535        },
3536
3537        // private
3538        register : function(menu){
3539            if(!menus){
3540                init();
3541            }
3542            menus[menu.id] = menu;
3543            menu.on("beforehide", onBeforeHide);
3544            menu.on("hide", onHide);
3545            menu.on("beforeshow", onBeforeShow);
3546            menu.on("show", onShow);
3547            var g = menu.group;
3548            if(g && menu.events["checkchange"]){
3549                if(!groups[g]){
3550                    groups[g] = [];
3551                }
3552                groups[g].push(menu);
3553                menu.on("checkchange", onCheck);
3554            }
3555        },
3556
3557         /**
3558          * Returns a {@link Roo.menu.Menu} object
3559          * @param {String/Object} menu The string menu id, an existing menu object reference, or a Menu config that will
3560          * be used to generate and return a new Menu instance.
3561          */
3562        get : function(menu){
3563            if(typeof menu == "string"){ // menu id
3564                return menus[menu];
3565            }else if(menu.events){  // menu instance
3566                return menu;
3567            }
3568            /*else if(typeof menu.length == 'number'){ // array of menu items?
3569                return new Roo.bootstrap.Menu({items:menu});
3570            }else{ // otherwise, must be a config
3571                return new Roo.bootstrap.Menu(menu);
3572            }
3573            */
3574            return false;
3575        },
3576
3577        // private
3578        unregister : function(menu){
3579            delete menus[menu.id];
3580            menu.un("beforehide", onBeforeHide);
3581            menu.un("hide", onHide);
3582            menu.un("beforeshow", onBeforeShow);
3583            menu.un("show", onShow);
3584            var g = menu.group;
3585            if(g && menu.events["checkchange"]){
3586                groups[g].remove(menu);
3587                menu.un("checkchange", onCheck);
3588            }
3589        },
3590
3591        // private
3592        registerCheckable : function(menuItem){
3593            var g = menuItem.group;
3594            if(g){
3595                if(!groups[g]){
3596                    groups[g] = [];
3597                }
3598                groups[g].push(menuItem);
3599                menuItem.on("beforecheckchange", onBeforeCheck);
3600            }
3601        },
3602
3603        // private
3604        unregisterCheckable : function(menuItem){
3605            var g = menuItem.group;
3606            if(g){
3607                groups[g].remove(menuItem);
3608                menuItem.un("beforecheckchange", onBeforeCheck);
3609            }
3610        }
3611    };
3612 }(); 
3613 /**
3614  * @class Roo.bootstrap.menu.Menu
3615  * @extends Roo.bootstrap.Component
3616  * @licence LGPL
3617  * @children Roo.bootstrap.menu.Item Roo.bootstrap.menu.Separator
3618  * @parent none
3619  * Bootstrap Menu class - container for MenuItems - normally has to be added to a object that supports the menu property
3620  * 
3621  * @cfg {String} type (dropdown|treeview|submenu) type of menu
3622  * @cfg {bool} hidden  if the menu should be hidden when rendered.
3623  * @cfg {bool} stopEvent (true|false)  Stop event after trigger press (default true)
3624  * @cfg {bool} isLink (true|false)  the menu has link disable auto expand and collaspe (default false)
3625 * @cfg {bool} hideTrigger (true|false)  default false - hide the carret for trigger.
3626 * @cfg {String} align  default tl-bl? == below  - how the menu should be aligned. 
3627  
3628  * @constructor
3629  * Create a new Menu
3630  * @param {Object} config The config objectQ
3631  */
3632
3633
3634 Roo.bootstrap.menu.Menu = function(config){
3635     
3636     if (config.type == 'treeview') {
3637         // normally menu's are drawn attached to the document to handle layering etc..
3638         // however treeview (used by the docs menu is drawn into the parent element)
3639         this.container_method = 'getChildContainer'; 
3640     }
3641     
3642     Roo.bootstrap.menu.Menu.superclass.constructor.call(this, config);
3643     if (this.registerMenu && this.type != 'treeview')  {
3644         Roo.bootstrap.menu.Manager.register(this);
3645     }
3646     
3647     
3648     this.addEvents({
3649         /**
3650          * @event beforeshow
3651          * Fires before this menu is displayed (return false to block)
3652          * @param {Roo.menu.Menu} this
3653          */
3654         beforeshow : true,
3655         /**
3656          * @event beforehide
3657          * Fires before this menu is hidden (return false to block)
3658          * @param {Roo.menu.Menu} this
3659          */
3660         beforehide : true,
3661         /**
3662          * @event show
3663          * Fires after this menu is displayed
3664          * @param {Roo.menu.Menu} this
3665          */
3666         show : true,
3667         /**
3668          * @event hide
3669          * Fires after this menu is hidden
3670          * @param {Roo.menu.Menu} this
3671          */
3672         hide : true,
3673         /**
3674          * @event click
3675          * Fires when this menu is clicked (or when the enter key is pressed while it is active)
3676          * @param {Roo.menu.Menu} this
3677          * @param {Roo.menu.Item} menuItem The menu item that was clicked
3678          * @param {Roo.EventObject} e
3679          */
3680         click : true,
3681         /**
3682          * @event mouseover
3683          * Fires when the mouse is hovering over this menu
3684          * @param {Roo.menu.Menu} this
3685          * @param {Roo.EventObject} e
3686          * @param {Roo.menu.Item} menuItem The menu item that was clicked
3687          */
3688         mouseover : true,
3689         /**
3690          * @event mouseout
3691          * Fires when the mouse exits this menu
3692          * @param {Roo.menu.Menu} this
3693          * @param {Roo.EventObject} e
3694          * @param {Roo.menu.Item} menuItem The menu item that was clicked
3695          */
3696         mouseout : true,
3697         /**
3698          * @event itemclick
3699          * Fires when a menu item contained in this menu is clicked
3700          * @param {Roo.menu.BaseItem} baseItem The BaseItem that was clicked
3701          * @param {Roo.EventObject} e
3702          */
3703         itemclick: true
3704     });
3705     this.menuitems = new Roo.util.MixedCollection(false, function(o) { return o.el.id; });
3706 };
3707
3708 Roo.extend(Roo.bootstrap.menu.Menu, Roo.bootstrap.Component,  {
3709     
3710    /// html : false,
3711    
3712     triggerEl : false,  // is this set by component builder? -- it should really be fetched from parent()???
3713     type: false,
3714     /**
3715      * @cfg {Boolean} registerMenu True (default) - means that clicking on screen etc. hides it.
3716      */
3717     registerMenu : true,
3718     
3719     menuItems :false, // stores the menu items..
3720     
3721     hidden:true,
3722         
3723     parentMenu : false,
3724     
3725     stopEvent : true,
3726     
3727     isLink : false,
3728     
3729     container_method : 'getDocumentBody', // so the menu is rendered on the body and zIndex works.
3730     
3731     hideTrigger : false,
3732     
3733     align : 'tl-bl?',
3734     
3735     
3736     getChildContainer : function() {
3737         return this.el;  
3738     },
3739     
3740     getAutoCreate : function(){
3741          
3742         //if (['right'].indexOf(this.align)!==-1) {
3743         //    cfg.cn[1].cls += ' pull-right'
3744         //}
3745          
3746         var cfg = {
3747             tag : 'ul',
3748             cls : 'dropdown-menu shadow' ,
3749             style : 'z-index:1000'
3750             
3751         };
3752         
3753         if (this.type === 'submenu') {
3754             cfg.cls = 'submenu active';
3755         }
3756         if (this.type === 'treeview') {
3757             cfg.cls = 'treeview-menu';
3758         }
3759         
3760         return cfg;
3761     },
3762     initEvents : function() {
3763         
3764        // Roo.log("ADD event");
3765        // Roo.log(this.triggerEl.dom);
3766         if (this.triggerEl) {
3767             
3768             this.triggerEl.on('click', this.onTriggerClick, this);
3769             
3770             this.triggerEl.on(Roo.isTouch ? 'touchstart' : 'mouseup', this.onTriggerPress, this);
3771             
3772             if (!this.hideTrigger) {
3773                 if (this.triggerEl.hasClass('nav-item') && this.triggerEl.select('.nav-link',true).length) {
3774                     // dropdown toggle on the 'a' in BS4?
3775                     this.triggerEl.select('.nav-link',true).first().addClass('dropdown-toggle');
3776                 } else {
3777                     this.triggerEl.addClass('dropdown-toggle');
3778                 }
3779             }
3780         }
3781         
3782         if (Roo.isTouch) {
3783             this.el.on('touchstart'  , this.onTouch, this);
3784         }
3785         this.el.on('click' , this.onClick, this);
3786
3787         this.el.on("mouseover", this.onMouseOver, this);
3788         this.el.on("mouseout", this.onMouseOut, this);
3789         
3790     },
3791     
3792     findTargetItem : function(e)
3793     {
3794         var t = e.getTarget(".dropdown-menu-item", this.el,  true);
3795         if(!t){
3796             return false;
3797         }
3798         //Roo.log(t);         Roo.log(t.id);
3799         if(t && t.id){
3800             //Roo.log(this.menuitems);
3801             return this.menuitems.get(t.id);
3802             
3803             //return this.items.get(t.menuItemId);
3804         }
3805         
3806         return false;
3807     },
3808     
3809     onTouch : function(e) 
3810     {
3811         Roo.log("menu.onTouch");
3812         //e.stopEvent(); this make the user popdown broken
3813         this.onClick(e);
3814     },
3815     
3816     onClick : function(e)
3817     {
3818         Roo.log("menu.onClick");
3819         
3820         var t = this.findTargetItem(e);
3821         if(!t || t.isContainer){
3822             return;
3823         }
3824         Roo.log(e);
3825         /*
3826         if (Roo.isTouch && e.type == 'touchstart' && t.menu  && !t.disabled) {
3827             if(t == this.activeItem && t.shouldDeactivate(e)){
3828                 this.activeItem.deactivate();
3829                 delete this.activeItem;
3830                 return;
3831             }
3832             if(t.canActivate){
3833                 this.setActiveItem(t, true);
3834             }
3835             return;
3836             
3837             
3838         }
3839         */
3840        
3841         Roo.log('pass click event');
3842         
3843         t.onClick(e);
3844         
3845         this.fireEvent("click", this, t, e);
3846         
3847         var _this = this;
3848         
3849         if(!t.href.length || t.href == '#'){
3850             (function() { _this.hide(); }).defer(100);
3851         }
3852         
3853     },
3854     
3855     onMouseOver : function(e){
3856         var t  = this.findTargetItem(e);
3857         //Roo.log(t);
3858         //if(t){
3859         //    if(t.canActivate && !t.disabled){
3860         //        this.setActiveItem(t, true);
3861         //    }
3862         //}
3863         
3864         this.fireEvent("mouseover", this, e, t);
3865     },
3866     isVisible : function(){
3867         return !this.hidden;
3868     },
3869     onMouseOut : function(e){
3870         var t  = this.findTargetItem(e);
3871         
3872         //if(t ){
3873         //    if(t == this.activeItem && t.shouldDeactivate(e)){
3874         //        this.activeItem.deactivate();
3875         //        delete this.activeItem;
3876         //    }
3877         //}
3878         this.fireEvent("mouseout", this, e, t);
3879     },
3880     
3881     
3882     /**
3883      * Displays this menu relative to another element
3884      * @param {String/HTMLElement/Roo.Element} element The element to align to
3885      * @param {String} position (optional) The {@link Roo.Element#alignTo} anchor position to use in aligning to
3886      * the element (defaults to this.defaultAlign)
3887      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
3888      */
3889     show : function(el, pos, parentMenu)
3890     {
3891         if (false === this.fireEvent("beforeshow", this)) {
3892             Roo.log("show canceled");
3893             return;
3894         }
3895         this.parentMenu = parentMenu;
3896         if(!this.el){
3897             this.render();
3898         }
3899         this.el.addClass('show'); // show otherwise we do not know how big we are..
3900          
3901         var xy = this.el.getAlignToXY(el, pos);
3902         
3903         // bl-tl << left align  below
3904         // tl-bl << left align 
3905         
3906         if(this.el.getWidth() + xy[0] >= Roo.lib.Dom.getViewWidth()){
3907             // if it goes to far to the right.. -> align left.
3908             xy = this.el.getAlignToXY(el, this.align.replace('/l/g', 'r'))
3909         }
3910         if(xy[0] < 0){
3911             // was left align - go right?
3912             xy = this.el.getAlignToXY(el, this.align.replace('/r/g', 'l'))
3913         }
3914         
3915         // goes down the bottom
3916         if(this.el.getHeight() + xy[1] >= Roo.lib.Dom.getViewHeight() ||
3917            xy[1]  < 0 ){
3918             var a = this.align.replace('?', '').split('-');
3919             xy = this.el.getAlignToXY(el, a[1]  + '-' + a[0] + '?')
3920             
3921         }
3922         
3923         this.showAt(  xy , parentMenu, false);
3924     },
3925      /**
3926      * Displays this menu at a specific xy position
3927      * @param {Array} xyPosition Contains X & Y [x, y] values for the position at which to show the menu (coordinates are page-based)
3928      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
3929      */
3930     showAt : function(xy, parentMenu, /* private: */_e){
3931         this.parentMenu = parentMenu;
3932         if(!this.el){
3933             this.render();
3934         }
3935         if(_e !== false){
3936             this.fireEvent("beforeshow", this);
3937             //xy = this.el.adjustForConstraints(xy);
3938         }
3939         
3940         //this.el.show();
3941         this.hideMenuItems();
3942         this.hidden = false;
3943         if (this.triggerEl) {
3944             this.triggerEl.addClass('open');
3945         }
3946         
3947         this.el.addClass('show');
3948         
3949         
3950         
3951         // reassign x when hitting right
3952         
3953         // reassign y when hitting bottom
3954         
3955         // but the list may align on trigger left or trigger top... should it be a properity?
3956         
3957         if(this.el.getStyle('top') != 'auto' && this.el.getStyle('top').slice(-1) != "%"){
3958             this.el.setXY(xy);
3959         }
3960         
3961         this.focus();
3962         this.fireEvent("show", this);
3963     },
3964     
3965     focus : function(){
3966         return;
3967         if(!this.hidden){
3968             this.doFocus.defer(50, this);
3969         }
3970     },
3971
3972     doFocus : function(){
3973         if(!this.hidden){
3974             this.focusEl.focus();
3975         }
3976     },
3977
3978     /**
3979      * Hides this menu and optionally all parent menus
3980      * @param {Boolean} deep (optional) True to hide all parent menus recursively, if any (defaults to false)
3981      */
3982     hide : function(deep)
3983     {
3984         if (false === this.fireEvent("beforehide", this)) {
3985             Roo.log("hide canceled");
3986             return;
3987         }
3988         this.hideMenuItems();
3989         if(this.el && this.isVisible()){
3990            
3991             if(this.activeItem){
3992                 this.activeItem.deactivate();
3993                 this.activeItem = null;
3994             }
3995             if (this.triggerEl) {
3996                 this.triggerEl.removeClass('open');
3997             }
3998             
3999             this.el.removeClass('show');
4000             this.hidden = true;
4001             this.fireEvent("hide", this);
4002         }
4003         if(deep === true && this.parentMenu){
4004             this.parentMenu.hide(true);
4005         }
4006     },
4007     
4008     onTriggerClick : function(e)
4009     {
4010         Roo.log('trigger click');
4011         
4012         var target = e.getTarget();
4013         
4014         Roo.log(target.nodeName.toLowerCase());
4015         
4016         if(target.nodeName.toLowerCase() === 'i'){
4017             e.preventDefault();
4018         }
4019         
4020     },
4021     
4022     onTriggerPress  : function(e)
4023     {
4024         Roo.log('trigger press');
4025         //Roo.log(e.getTarget());
4026        // Roo.log(this.triggerEl.dom);
4027        
4028         // trigger only occurs on normal menu's -- if it's a treeview or dropdown... do not hide/show..
4029         var pel = Roo.get(e.getTarget());
4030         if (pel.findParent('.dropdown-menu') || pel.findParent('.treeview-menu') ) {
4031             Roo.log('is treeview or dropdown?');
4032             return;
4033         }
4034         
4035         if(e.getTarget().nodeName.toLowerCase() !== 'i' && this.isLink){
4036             return;
4037         }
4038         
4039         if (this.isVisible()) {
4040             Roo.log('hide');
4041             this.hide();
4042         } else {
4043             Roo.log('show');
4044             
4045             this.show(this.triggerEl, this.align, false);
4046         }
4047         
4048         if(this.stopEvent || e.getTarget().nodeName.toLowerCase() === 'i'){
4049             e.stopEvent();
4050         }
4051         
4052     },
4053        
4054     
4055     hideMenuItems : function()
4056     {
4057         Roo.log("hide Menu Items");
4058         if (!this.el) { 
4059             return;
4060         }
4061         
4062         this.el.select('.open',true).each(function(aa) {
4063             
4064             aa.removeClass('open');
4065          
4066         });
4067     },
4068     addxtypeChild : function (tree, cntr) {
4069         var comp= Roo.bootstrap.menu.Menu.superclass.addxtypeChild.call(this, tree, cntr);
4070           
4071         this.menuitems.add(comp);
4072         return comp;
4073
4074     },
4075     getEl : function()
4076     {
4077         Roo.log(this.el);
4078         return this.el;
4079     },
4080     
4081     clear : function()
4082     {
4083         this.getEl().dom.innerHTML = '';
4084         this.menuitems.clear();
4085     }
4086 });
4087
4088  
4089  /**
4090  * @class Roo.bootstrap.menu.Item
4091  * @extends Roo.bootstrap.Component
4092  * @children  Roo.bootstrap.Button Roo.bootstrap.ButtonUploader Roo.bootstrap.Row Roo.bootstrap.Column Roo.bootstrap.Container
4093  * @parent Roo.bootstrap.menu.Menu
4094  * @licence LGPL
4095  * Bootstrap MenuItem class
4096  * 
4097  * @cfg {String} html the menu label
4098  * @cfg {String} href the link
4099  * @cfg {Boolean} preventDefault do not trigger A href on clicks (default false).
4100  * @cfg {Boolean} isContainer is it a container - just returns a drop down item..
4101  * @cfg {Boolean} active  used on sidebars to highlight active itesm
4102  * @cfg {String} fa favicon to show on left of menu item.
4103  * @cfg {Roo.bootsrap.Menu} menu the child menu.
4104  * 
4105  * 
4106  * @constructor
4107  * Create a new MenuItem
4108  * @param {Object} config The config object
4109  */
4110
4111
4112 Roo.bootstrap.menu.Item = function(config){
4113     Roo.bootstrap.menu.Item.superclass.constructor.call(this, config);
4114     this.addEvents({
4115         // raw events
4116         /**
4117          * @event click
4118          * The raw click event for the entire grid.
4119          * @param {Roo.bootstrap.menu.Item} this
4120          * @param {Roo.EventObject} e
4121          */
4122         "click" : true
4123     });
4124 };
4125
4126 Roo.extend(Roo.bootstrap.menu.Item, Roo.bootstrap.Component,  {
4127     
4128     href : false,
4129     html : false,
4130     preventDefault: false,
4131     isContainer : false,
4132     active : false,
4133     fa: false,
4134     
4135     getAutoCreate : function(){
4136         
4137         if(this.isContainer){
4138             return {
4139                 tag: 'li',
4140                 cls: 'dropdown-menu-item '
4141             };
4142         }
4143         var ctag = {
4144             tag: 'span',
4145             html: 'Link'
4146         };
4147         
4148         var anc = {
4149             tag : 'a',
4150             cls : 'dropdown-item',
4151             href : '#',
4152             cn : [  ]
4153         };
4154         
4155         if (this.fa !== false) {
4156             anc.cn.push({
4157                 tag : 'i',
4158                 cls : 'fa fa-' + this.fa
4159             });
4160         }
4161         
4162         anc.cn.push(ctag);
4163         
4164         
4165         var cfg= {
4166             tag: 'li',
4167             cls: 'dropdown-menu-item',
4168             cn: [ anc ]
4169         };
4170         if (this.parent().type == 'treeview') {
4171             cfg.cls = 'treeview-menu';
4172         }
4173         if (this.active) {
4174             cfg.cls += ' active';
4175         }
4176         
4177         
4178         
4179         anc.href = this.href || cfg.cn[0].href ;
4180         ctag.html = this.html || cfg.cn[0].html ;
4181         return cfg;
4182     },
4183     
4184     initEvents: function()
4185     {
4186         if (this.parent().type == 'treeview') {
4187             this.el.select('a').on('click', this.onClick, this);
4188         }
4189         
4190         if (this.menu) {
4191             this.menu.parentType = this.xtype;
4192             this.menu.triggerEl = this.el;
4193             this.menu = this.addxtype(Roo.apply({}, this.menu));
4194         }
4195         
4196     },
4197     onClick : function(e)
4198     {
4199         //Roo.log('item on click ');
4200         
4201         if(this.href === false || this.preventDefault){
4202             e.preventDefault();
4203         }
4204         //this.parent().hideMenuItems();
4205         
4206         this.fireEvent('click', this, e);
4207     },
4208     getEl : function()
4209     {
4210         return this.el;
4211     } 
4212 });
4213
4214  
4215
4216  
4217
4218   
4219 /**
4220  * @class Roo.bootstrap.menu.Separator
4221  * @extends Roo.bootstrap.Component
4222  * @licence LGPL
4223  * @parent Roo.bootstrap.menu.Menu
4224  * Bootstrap Separator class
4225  * 
4226  * @constructor
4227  * Create a new Separator
4228  * @param {Object} config The config object
4229  */
4230
4231
4232 Roo.bootstrap.menu.Separator = function(config){
4233     Roo.bootstrap.menu.Separator.superclass.constructor.call(this, config);
4234 };
4235
4236 Roo.extend(Roo.bootstrap.menu.Separator, Roo.bootstrap.Component,  {
4237     
4238     getAutoCreate : function(){
4239         var cfg = {
4240             tag : 'li',
4241             cls: 'dropdown-divider divider'
4242         };
4243         
4244         return cfg;
4245     }
4246    
4247 });
4248
4249  
4250
4251  
4252 /*
4253 * Licence: LGPL
4254 */
4255
4256 /**
4257  * @class Roo.bootstrap.Modal
4258  * @extends Roo.bootstrap.Component
4259  * @parent none builder
4260  * @children Roo.bootstrap.Component
4261  * Bootstrap Modal class
4262  * @cfg {String} title Title of dialog
4263  * @cfg {String} html - the body of the dialog (for simple ones) - you can also use template..
4264  * @cfg {Roo.Template} tmpl - a template with variables. to use it, add a handler in show:method  adn
4265  * @cfg {Boolean} specificTitle default false
4266  * @cfg {Roo.bootstrap.Button} buttons[] Array of buttons or standard button set..
4267  * @cfg {String} buttonPosition (left|right|center) default right (DEPRICATED) - use mr-auto on buttons to put them on the left
4268  * @cfg {Boolean} animate default true
4269  * @cfg {Boolean} allow_close default true
4270  * @cfg {Boolean} fitwindow default false
4271  * @cfg {Boolean} bodyOverflow should the body element have overflow auto added default false
4272  * @cfg {Number} width fixed width - usefull for chrome extension only really.
4273  * @cfg {Number} height fixed height - usefull for chrome extension only really.
4274  * @cfg {String} size (sm|lg|xl) default empty
4275  * @cfg {Number} max_width set the max width of modal
4276  * @cfg {Boolean} editableTitle can the title be edited
4277
4278  *
4279  *
4280  * @constructor
4281  * Create a new Modal Dialog
4282  * @param {Object} config The config object
4283  */
4284
4285 Roo.bootstrap.Modal = function(config){
4286     Roo.bootstrap.Modal.superclass.constructor.call(this, config);
4287     this.addEvents({
4288         // raw events
4289         /**
4290          * @event btnclick
4291          * The raw btnclick event for the button
4292          * @param {Roo.EventObject} e
4293          */
4294         "btnclick" : true,
4295         /**
4296          * @event resize
4297          * Fire when dialog resize
4298          * @param {Roo.bootstrap.Modal} this
4299          * @param {Roo.EventObject} e
4300          */
4301         "resize" : true,
4302         /**
4303          * @event titlechanged
4304          * Fire when the editable title has been changed
4305          * @param {Roo.bootstrap.Modal} this
4306          * @param {Roo.EventObject} value
4307          */
4308         "titlechanged" : true 
4309         
4310     });
4311     this.buttons = this.buttons || [];
4312
4313     if (this.tmpl) {
4314         this.tmpl = Roo.factory(this.tmpl);
4315     }
4316
4317 };
4318
4319 Roo.extend(Roo.bootstrap.Modal, Roo.bootstrap.Component,  {
4320
4321     title : 'test dialog',
4322
4323     buttons : false,
4324
4325     // set on load...
4326
4327     html: false,
4328
4329     tmp: false,
4330
4331     specificTitle: false,
4332
4333     buttonPosition: 'right',
4334
4335     allow_close : true,
4336
4337     animate : true,
4338
4339     fitwindow: false,
4340     
4341      // private
4342     dialogEl: false,
4343     bodyEl:  false,
4344     footerEl:  false,
4345     titleEl:  false,
4346     closeEl:  false,
4347
4348     size: '',
4349     
4350     max_width: 0,
4351     
4352     max_height: 0,
4353     
4354     fit_content: false,
4355     editableTitle  : false,
4356
4357     onRender : function(ct, position)
4358     {
4359         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
4360
4361         if(!this.el){
4362             var cfg = Roo.apply({},  this.getAutoCreate());
4363             cfg.id = Roo.id();
4364             //if(!cfg.name){
4365             //    cfg.name = typeof(this.name) == 'undefined' ? this.id : this.name;
4366             //}
4367             //if (!cfg.name.length) {
4368             //    delete cfg.name;
4369            // }
4370             if (this.cls) {
4371                 cfg.cls += ' ' + this.cls;
4372             }
4373             if (this.style) {
4374                 cfg.style = this.style;
4375             }
4376             this.el = Roo.get(document.body).createChild(cfg, position);
4377         }
4378         //var type = this.el.dom.type;
4379
4380
4381         if(this.tabIndex !== undefined){
4382             this.el.dom.setAttribute('tabIndex', this.tabIndex);
4383         }
4384
4385         this.dialogEl = this.el.select('.modal-dialog',true).first();
4386         this.bodyEl = this.el.select('.modal-body',true).first();
4387         this.closeEl = this.el.select('.modal-header .close', true).first();
4388         this.headerEl = this.el.select('.modal-header',true).first();
4389         this.titleEl = this.el.select('.modal-title',true).first();
4390         this.footerEl = this.el.select('.modal-footer',true).first();
4391
4392         this.maskEl = Roo.DomHelper.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
4393         
4394         //this.el.addClass("x-dlg-modal");
4395
4396         if (this.buttons.length) {
4397             Roo.each(this.buttons, function(bb) {
4398                 var b = Roo.apply({}, bb);
4399                 b.xns = b.xns || Roo.bootstrap;
4400                 b.xtype = b.xtype || 'Button';
4401                 if (typeof(b.listeners) == 'undefined') {
4402                     b.listeners = { click : this.onButtonClick.createDelegate(this)  };
4403                 }
4404
4405                 var btn = Roo.factory(b);
4406
4407                 btn.render(this.getButtonContainer());
4408
4409             },this);
4410         }
4411         // render the children.
4412         var nitems = [];
4413
4414         if(typeof(this.items) != 'undefined'){
4415             var items = this.items;
4416             delete this.items;
4417
4418             for(var i =0;i < items.length;i++) {
4419                 // we force children not to montor widnow resize  - as we do that for them.
4420                 items[i].monitorWindowResize = false;
4421                 nitems.push(this.addxtype(Roo.apply({}, items[i])));
4422             }
4423         }
4424
4425         this.items = nitems;
4426
4427         // where are these used - they used to be body/close/footer
4428
4429
4430         this.initEvents();
4431         //this.el.addClass([this.fieldClass, this.cls]);
4432
4433     },
4434
4435     getAutoCreate : function()
4436     {
4437         // we will default to modal-body-overflow - might need to remove or make optional later.
4438         var bdy = {
4439                 cls : 'modal-body ' + (this.bodyOverflow ? 'overflow-auto' : ''), 
4440                 html : this.html || ''
4441         };
4442
4443         var title = {
4444             tag: 'h5',
4445             cls : 'modal-title',
4446             html : this.title
4447         };
4448
4449         if(this.specificTitle){ // WTF is this?
4450             title = this.title;
4451         }
4452
4453         var header = [];
4454         if (this.allow_close && Roo.bootstrap.version == 3) {
4455             header.push({
4456                 tag: 'button',
4457                 cls : 'close',
4458                 html : '&times'
4459             });
4460         }
4461
4462         header.push(title);
4463
4464         if (this.editableTitle) {
4465             header.push({
4466                 cls: 'form-control roo-editable-title d-none',
4467                 tag: 'input',
4468                 type: 'text'
4469             });
4470         }
4471         
4472         if (this.allow_close && Roo.bootstrap.version == 4) {
4473             header.push({
4474                 tag: 'button',
4475                 cls : 'close',
4476                 html : '&times'
4477             });
4478         }
4479         
4480         var size = '';
4481
4482         if(this.size.length){
4483             size = 'modal-' + this.size;
4484         }
4485         
4486         var footer = Roo.bootstrap.version == 3 ?
4487             {
4488                 cls : 'modal-footer',
4489                 cn : [
4490                     {
4491                         tag: 'div',
4492                         cls: 'btn-' + this.buttonPosition
4493                     }
4494                 ]
4495
4496             } :
4497             {  // BS4 uses mr-auto on left buttons....
4498                 cls : 'modal-footer'
4499             };
4500
4501             
4502
4503         
4504         
4505         var modal = {
4506             cls: "modal",
4507              cn : [
4508                 {
4509                     cls: "modal-dialog " + size,
4510                     cn : [
4511                         {
4512                             cls : "modal-content",
4513                             cn : [
4514                                 {
4515                                     cls : 'modal-header',
4516                                     cn : header
4517                                 },
4518                                 bdy,
4519                                 footer
4520                             ]
4521
4522                         }
4523                     ]
4524
4525                 }
4526             ]
4527         };
4528
4529         if(this.animate){
4530             modal.cls += ' fade';
4531         }
4532
4533         return modal;
4534
4535     },
4536     getChildContainer : function() {
4537
4538          return this.bodyEl;
4539
4540     },
4541     getButtonContainer : function() {
4542         
4543          return Roo.bootstrap.version == 4 ?
4544             this.el.select('.modal-footer',true).first()
4545             : this.el.select('.modal-footer div',true).first();
4546
4547     },
4548     initEvents : function()
4549     {
4550         if (this.allow_close) {
4551             this.closeEl.on('click', this.hide, this);
4552         }
4553         Roo.EventManager.onWindowResize(this.resize, this, true);
4554         if (this.editableTitle) {
4555             this.headerEditEl =  this.headerEl.select('.form-control',true).first();
4556             this.headerEl.on('click', function() { this.toggleHeaderInput(true) } , this);
4557             this.headerEditEl.on('keyup', function(e) {
4558                     if([  e.RETURN , e.TAB , e.ESC ].indexOf(e.keyCode) > -1) {
4559                         this.toggleHeaderInput(false)
4560                     }
4561                 }, this);
4562             this.headerEditEl.on('blur', function(e) {
4563                 this.toggleHeaderInput(false)
4564             },this);
4565         }
4566
4567     },
4568   
4569
4570     resize : function()
4571     {
4572         this.maskEl.setSize(
4573             Roo.lib.Dom.getViewWidth(true),
4574             Roo.lib.Dom.getViewHeight(true)
4575         );
4576         
4577         if (this.fitwindow) {
4578             
4579            this.dialogEl.setStyle( { 'max-width' : '100%' });
4580             this.setSize(
4581                 this.width || Roo.lib.Dom.getViewportWidth(true) - 30,
4582                 this.height || Roo.lib.Dom.getViewportHeight(true) // catering margin-top 30 margin-bottom 30
4583             );
4584             return;
4585         }
4586         
4587         if(this.max_width !== 0) {
4588             
4589             var w = Math.min(this.max_width, Roo.lib.Dom.getViewportWidth(true) - 30);
4590             
4591             if(this.height) {
4592                 this.setSize(w, this.height);
4593                 return;
4594             }
4595             
4596             if(this.max_height) {
4597                 this.setSize(w,Math.min(
4598                     this.max_height,
4599                     Roo.lib.Dom.getViewportHeight(true) - 60
4600                 ));
4601                 
4602                 return;
4603             }
4604             
4605             if(!this.fit_content) {
4606                 this.setSize(w, Roo.lib.Dom.getViewportHeight(true) - 60);
4607                 return;
4608             }
4609             
4610             this.setSize(w, Math.min(
4611                 60 +
4612                 this.headerEl.getHeight() + 
4613                 this.footerEl.getHeight() + 
4614                 this.getChildHeight(this.bodyEl.dom.childNodes),
4615                 Roo.lib.Dom.getViewportHeight(true) - 60)
4616             );
4617         }
4618         
4619     },
4620
4621     setSize : function(w,h)
4622     {
4623         if (!w && !h) {
4624             return;
4625         }
4626         
4627         this.resizeTo(w,h);
4628         // any layout/border etc.. resize..
4629         (function () {
4630             this.items.forEach( function(e) {
4631                 e.layout ? e.layout() : false;
4632
4633             });
4634         }).defer(100,this);
4635         
4636     },
4637
4638     show : function() {
4639
4640         if (!this.rendered) {
4641             this.render();
4642         }
4643         this.toggleHeaderInput(false);
4644         //this.el.setStyle('display', 'block');
4645         this.el.removeClass('hideing');
4646         this.el.dom.style.display='block';
4647         
4648         Roo.get(document.body).addClass('modal-open');
4649  
4650         if(this.animate){  // element has 'fade'  - so stuff happens after .3s ?- not sure why the delay?
4651             
4652             (function(){
4653                 this.el.addClass('show');
4654                 this.el.addClass('in');
4655             }).defer(50, this);
4656         }else{
4657             this.el.addClass('show');
4658             this.el.addClass('in');
4659         }
4660
4661         // not sure how we can show data in here..
4662         //if (this.tmpl) {
4663         //    this.getChildContainer().dom.innerHTML = this.tmpl.applyTemplate(this);
4664         //}
4665
4666         Roo.get(document.body).addClass("x-body-masked");
4667         
4668         this.maskEl.setSize(Roo.lib.Dom.getViewWidth(true),   Roo.lib.Dom.getViewHeight(true));
4669         this.maskEl.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
4670         this.maskEl.dom.style.display = 'block';
4671         this.maskEl.addClass('show');
4672         
4673         
4674         this.resize();
4675         
4676         this.fireEvent('show', this);
4677
4678         // set zindex here - otherwise it appears to be ignored...
4679         this.el.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
4680         
4681         
4682         // this is for children that are... layout.Border 
4683         (function () {
4684             this.items.forEach( function(e) {
4685                 e.layout ? e.layout() : false;
4686
4687             });
4688         }).defer(100,this);
4689
4690     },
4691     hide : function()
4692     {
4693         if(this.fireEvent("beforehide", this) !== false){
4694             
4695             this.maskEl.removeClass('show');
4696             
4697             this.maskEl.dom.style.display = '';
4698             Roo.get(document.body).removeClass("x-body-masked");
4699             this.el.removeClass('in');
4700             this.el.select('.modal-dialog', true).first().setStyle('transform','');
4701
4702             if(this.animate){ // why
4703                 this.el.addClass('hideing');
4704                 this.el.removeClass('show');
4705                 (function(){
4706                     if (!this.el.hasClass('hideing')) {
4707                         return; // it's been shown again...
4708                     }
4709                     
4710                     this.el.dom.style.display='';
4711
4712                     Roo.get(document.body).removeClass('modal-open');
4713                     this.el.removeClass('hideing');
4714                 }).defer(150,this);
4715                 
4716             }else{
4717                 this.el.removeClass('show');
4718                 this.el.dom.style.display='';
4719                 Roo.get(document.body).removeClass('modal-open');
4720
4721             }
4722             this.fireEvent('hide', this);
4723         }
4724     },
4725     isVisible : function()
4726     {
4727         
4728         return this.el.hasClass('show') && !this.el.hasClass('hideing');
4729         
4730     },
4731
4732     addButton : function(str, cb)
4733     {
4734
4735
4736         var b = Roo.apply({}, { html : str } );
4737         b.xns = b.xns || Roo.bootstrap;
4738         b.xtype = b.xtype || 'Button';
4739         if (typeof(b.listeners) == 'undefined') {
4740             b.listeners = { click : cb.createDelegate(this)  };
4741         }
4742
4743         var btn = Roo.factory(b);
4744
4745         btn.render(this.getButtonContainer());
4746
4747         return btn;
4748
4749     },
4750
4751     setDefaultButton : function(btn)
4752     {
4753         //this.el.select('.modal-footer').()
4754     },
4755
4756     resizeTo: function(w,h)
4757     {
4758         this.dialogEl.setWidth(w);
4759         
4760         var diff = this.headerEl.getHeight() + this.footerEl.getHeight() + 60; // dialog margin-bottom: 30  
4761
4762         this.bodyEl.setHeight(h - diff);
4763         
4764         this.fireEvent('resize', this);
4765     },
4766     
4767     setContentSize  : function(w, h)
4768     {
4769
4770     },
4771     onButtonClick: function(btn,e)
4772     {
4773         //Roo.log([a,b,c]);
4774         this.fireEvent('btnclick', btn.name, e);
4775     },
4776      /**
4777      * Set the title of the Dialog
4778      * @param {String} str new Title
4779      */
4780     setTitle: function(str) {
4781         this.titleEl.dom.innerHTML = str;
4782         this.title = str;
4783     },
4784     /**
4785      * Set the body of the Dialog
4786      * @param {String} str new Title
4787      */
4788     setBody: function(str) {
4789         this.bodyEl.dom.innerHTML = str;
4790     },
4791     /**
4792      * Set the body of the Dialog using the template
4793      * @param {Obj} data - apply this data to the template and replace the body contents.
4794      */
4795     applyBody: function(obj)
4796     {
4797         if (!this.tmpl) {
4798             Roo.log("Error - using apply Body without a template");
4799             //code
4800         }
4801         this.tmpl.overwrite(this.bodyEl, obj);
4802     },
4803     
4804     getChildHeight : function(child_nodes)
4805     {
4806         if(
4807             !child_nodes ||
4808             child_nodes.length == 0
4809         ) {
4810             return 0;
4811         }
4812         
4813         var child_height = 0;
4814         
4815         for(var i = 0; i < child_nodes.length; i++) {
4816             
4817             /*
4818             * for modal with tabs...
4819             if(child_nodes[i].classList.contains('roo-layout-panel')) {
4820                 
4821                 var layout_childs = child_nodes[i].childNodes;
4822                 
4823                 for(var j = 0; j < layout_childs.length; j++) {
4824                     
4825                     if(layout_childs[j].classList.contains('roo-layout-panel-body')) {
4826                         
4827                         var layout_body_childs = layout_childs[j].childNodes;
4828                         
4829                         for(var k = 0; k < layout_body_childs.length; k++) {
4830                             
4831                             if(layout_body_childs[k].classList.contains('navbar')) {
4832                                 child_height += layout_body_childs[k].offsetHeight;
4833                                 continue;
4834                             }
4835                             
4836                             if(layout_body_childs[k].classList.contains('roo-layout-tabs-body')) {
4837                                 
4838                                 var layout_body_tab_childs = layout_body_childs[k].childNodes;
4839                                 
4840                                 for(var m = 0; m < layout_body_tab_childs.length; m++) {
4841                                     
4842                                     if(layout_body_tab_childs[m].classList.contains('roo-layout-active-content')) {
4843                                         child_height += this.getChildHeight(layout_body_tab_childs[m].childNodes);
4844                                         continue;
4845                                     }
4846                                     
4847                                 }
4848                                 
4849                             }
4850                             
4851                         }
4852                     }
4853                 }
4854                 continue;
4855             }
4856             */
4857             
4858             child_height += child_nodes[i].offsetHeight;
4859             // Roo.log(child_nodes[i].offsetHeight);
4860         }
4861         
4862         return child_height;
4863     },
4864     toggleHeaderInput : function(is_edit)
4865     {
4866         if (!this.editableTitle) {
4867             return; // not editable.
4868         }
4869         if (is_edit && this.is_header_editing) {
4870             return; // already editing..
4871         }
4872         if (is_edit) {
4873     
4874             this.headerEditEl.dom.value = this.title;
4875             this.headerEditEl.removeClass('d-none');
4876             this.headerEditEl.dom.focus();
4877             this.titleEl.addClass('d-none');
4878             
4879             this.is_header_editing = true;
4880             return
4881         }
4882         // flip back to not editing.
4883         this.title = this.headerEditEl.dom.value;
4884         this.headerEditEl.addClass('d-none');
4885         this.titleEl.removeClass('d-none');
4886         this.titleEl.dom.innerHTML = String.format('{0}', this.title);
4887         this.is_header_editing = false;
4888         this.fireEvent('titlechanged', this, this.title);
4889     
4890             
4891         
4892     }
4893
4894 });
4895
4896
4897 Roo.apply(Roo.bootstrap.Modal,  {
4898     /**
4899          * Button config that displays a single OK button
4900          * @type Object
4901          */
4902         OK :  [{
4903             name : 'ok',
4904             weight : 'primary',
4905             html : 'OK'
4906         }],
4907         /**
4908          * Button config that displays Yes and No buttons
4909          * @type Object
4910          */
4911         YESNO : [
4912             {
4913                 name  : 'no',
4914                 html : 'No'
4915             },
4916             {
4917                 name  :'yes',
4918                 weight : 'primary',
4919                 html : 'Yes'
4920             }
4921         ],
4922
4923         /**
4924          * Button config that displays OK and Cancel buttons
4925          * @type Object
4926          */
4927         OKCANCEL : [
4928             {
4929                name : 'cancel',
4930                 html : 'Cancel'
4931             },
4932             {
4933                 name : 'ok',
4934                 weight : 'primary',
4935                 html : 'OK'
4936             }
4937         ],
4938         /**
4939          * Button config that displays Yes, No and Cancel buttons
4940          * @type Object
4941          */
4942         YESNOCANCEL : [
4943             {
4944                 name : 'yes',
4945                 weight : 'primary',
4946                 html : 'Yes'
4947             },
4948             {
4949                 name : 'no',
4950                 html : 'No'
4951             },
4952             {
4953                 name : 'cancel',
4954                 html : 'Cancel'
4955             }
4956         ],
4957         
4958         zIndex : 10001
4959 });
4960
4961 /*
4962  * - LGPL
4963  *
4964  * messagebox - can be used as a replace
4965  * 
4966  */
4967 /**
4968  * @class Roo.MessageBox
4969  * Utility class for generating different styles of message boxes.  The alias Roo.Msg can also be used.
4970  * Example usage:
4971  *<pre><code>
4972 // Basic alert:
4973 Roo.Msg.alert('Status', 'Changes saved successfully.');
4974
4975 // Prompt for user data:
4976 Roo.Msg.prompt('Name', 'Please enter your name:', function(btn, text){
4977     if (btn == 'ok'){
4978         // process text value...
4979     }
4980 });
4981
4982 // Show a dialog using config options:
4983 Roo.Msg.show({
4984    title:'Save Changes?',
4985    msg: 'Your are closing a tab that has unsaved changes. Would you like to save your changes?',
4986    buttons: Roo.Msg.YESNOCANCEL,
4987    fn: processResult,
4988    animEl: 'elId'
4989 });
4990 </code></pre>
4991  * @static
4992  */
4993 Roo.bootstrap.MessageBox = function(){
4994     var dlg, opt, mask, waitTimer;
4995     var bodyEl, msgEl, textboxEl, textareaEl, progressEl, pp;
4996     var buttons, activeTextEl, bwidth;
4997
4998     
4999     // private
5000     var handleButton = function(button){
5001         dlg.hide();
5002         Roo.callback(opt.fn, opt.scope||window, [button, activeTextEl.dom.value], 1);
5003     };
5004
5005     // private
5006     var handleHide = function(){
5007         if(opt && opt.cls){
5008             dlg.el.removeClass(opt.cls);
5009         }
5010         //if(waitTimer){
5011         //    Roo.TaskMgr.stop(waitTimer);
5012         //    waitTimer = null;
5013         //}
5014     };
5015
5016     // private
5017     var updateButtons = function(b){
5018         var width = 0;
5019         if(!b){
5020             buttons["ok"].hide();
5021             buttons["cancel"].hide();
5022             buttons["yes"].hide();
5023             buttons["no"].hide();
5024             dlg.footerEl.hide();
5025             
5026             return width;
5027         }
5028         dlg.footerEl.show();
5029         for(var k in buttons){
5030             if(typeof buttons[k] != "function"){
5031                 if(b[k]){
5032                     buttons[k].show();
5033                     buttons[k].setText(typeof b[k] == "string" ? b[k] : Roo.bootstrap.MessageBox.buttonText[k]);
5034                     width += buttons[k].el.getWidth()+15;
5035                 }else{
5036                     buttons[k].hide();
5037                 }
5038             }
5039         }
5040         return width;
5041     };
5042
5043     // private
5044     var handleEsc = function(d, k, e){
5045         if(opt && opt.closable !== false){
5046             dlg.hide();
5047         }
5048         if(e){
5049             e.stopEvent();
5050         }
5051     };
5052
5053     return {
5054         /**
5055          * Returns a reference to the underlying {@link Roo.BasicDialog} element
5056          * @return {Roo.BasicDialog} The BasicDialog element
5057          */
5058         getDialog : function(){
5059            if(!dlg){
5060                 dlg = new Roo.bootstrap.Modal( {
5061                     //draggable: true,
5062                     //resizable:false,
5063                     //constraintoviewport:false,
5064                     //fixedcenter:true,
5065                     //collapsible : false,
5066                     //shim:true,
5067                     //modal: true,
5068                 //    width: 'auto',
5069                   //  height:100,
5070                     //buttonAlign:"center",
5071                     closeClick : function(){
5072                         if(opt && opt.buttons && opt.buttons.no && !opt.buttons.cancel){
5073                             handleButton("no");
5074                         }else{
5075                             handleButton("cancel");
5076                         }
5077                     }
5078                 });
5079                 dlg.render();
5080                 dlg.on("hide", handleHide);
5081                 mask = dlg.mask;
5082                 //dlg.addKeyListener(27, handleEsc);
5083                 buttons = {};
5084                 this.buttons = buttons;
5085                 var bt = this.buttonText;
5086                 buttons["ok"] = dlg.addButton(bt["ok"], handleButton.createCallback("ok"));
5087                 buttons["yes"] = dlg.addButton(bt["yes"], handleButton.createCallback("yes"));
5088                 buttons["no"] = dlg.addButton(bt["no"], handleButton.createCallback("no"));
5089                 buttons["cancel"] = dlg.addButton(bt["cancel"], handleButton.createCallback("cancel"));
5090                 //Roo.log(buttons);
5091                 bodyEl = dlg.bodyEl.createChild({
5092
5093                     html:'<span class="roo-mb-text"></span><br /><input type="text" class="roo-mb-input" />' +
5094                         '<textarea class="roo-mb-textarea"></textarea>' +
5095                         '<div class="roo-mb-progress-wrap"><div class="roo-mb-progress"><div class="roo-mb-progress-bar">&#160;</div></div></div>'
5096                 });
5097                 msgEl = bodyEl.dom.firstChild;
5098                 textboxEl = Roo.get(bodyEl.dom.childNodes[2]);
5099                 textboxEl.enableDisplayMode();
5100                 textboxEl.addKeyListener([10,13], function(){
5101                     if(dlg.isVisible() && opt && opt.buttons){
5102                         if(opt.buttons.ok){
5103                             handleButton("ok");
5104                         }else if(opt.buttons.yes){
5105                             handleButton("yes");
5106                         }
5107                     }
5108                 });
5109                 textareaEl = Roo.get(bodyEl.dom.childNodes[3]);
5110                 textareaEl.enableDisplayMode();
5111                 progressEl = Roo.get(bodyEl.dom.childNodes[4]);
5112                 progressEl.enableDisplayMode();
5113                 
5114                 // This is supposed to be the progessElement.. but I think it's controlling the height of everything..
5115                 var pf = progressEl.dom.firstChild;
5116                 if (pf) {
5117                     pp = Roo.get(pf.firstChild);
5118                     pp.setHeight(pf.offsetHeight);
5119                 }
5120                 
5121             }
5122             return dlg;
5123         },
5124
5125         /**
5126          * Updates the message box body text
5127          * @param {String} text (optional) Replaces the message box element's innerHTML with the specified string (defaults to
5128          * the XHTML-compliant non-breaking space character '&amp;#160;')
5129          * @return {Roo.MessageBox} This message box
5130          */
5131         updateText : function(text)
5132         {
5133             if(!dlg.isVisible() && !opt.width){
5134                 dlg.dialogEl.setStyle({ 'max-width' : this.maxWidth});
5135                 // dlg.resizeTo(this.maxWidth, 100); // forcing the height breaks long alerts()
5136             }
5137             msgEl.innerHTML = text || '&#160;';
5138       
5139             var cw =  Math.max(msgEl.offsetWidth, msgEl.parentNode.scrollWidth);
5140             //Roo.log("guesed size: " + JSON.stringify([cw,msgEl.offsetWidth, msgEl.parentNode.scrollWidth]));
5141             var w = Math.max(
5142                     Math.min(opt.width || cw , this.maxWidth), 
5143                     Math.max(opt.minWidth || this.minWidth, bwidth)
5144             );
5145             if(opt.prompt){
5146                 activeTextEl.setWidth(w);
5147             }
5148             if(dlg.isVisible()){
5149                 dlg.fixedcenter = false;
5150             }
5151             // to big, make it scroll. = But as usual stupid IE does not support
5152             // !important..
5153             
5154             if ( bodyEl.getHeight() > (Roo.lib.Dom.getViewHeight() - 100)) {
5155                 bodyEl.setHeight ( Roo.lib.Dom.getViewHeight() - 100 );
5156                 bodyEl.dom.style.overflowY = 'auto' + ( Roo.isIE ? '' : ' !important');
5157             } else {
5158                 bodyEl.dom.style.height = '';
5159                 bodyEl.dom.style.overflowY = '';
5160             }
5161             if (cw > w) {
5162                 bodyEl.dom.style.get = 'auto' + ( Roo.isIE ? '' : ' !important');
5163             } else {
5164                 bodyEl.dom.style.overflowX = '';
5165             }
5166             
5167             dlg.setContentSize(w, bodyEl.getHeight());
5168             if(dlg.isVisible()){
5169                 dlg.fixedcenter = true;
5170             }
5171             return this;
5172         },
5173
5174         /**
5175          * Updates a progress-style message box's text and progress bar.  Only relevant on message boxes
5176          * initiated via {@link Roo.MessageBox#progress} or by calling {@link Roo.MessageBox#show} with progress: true.
5177          * @param {Number} value Any number between 0 and 1 (e.g., .5)
5178          * @param {String} text (optional) If defined, the message box's body text is replaced with the specified string (defaults to undefined)
5179          * @return {Roo.MessageBox} This message box
5180          */
5181         updateProgress : function(value, text){
5182             if(text){
5183                 this.updateText(text);
5184             }
5185             
5186             if (pp) { // weird bug on my firefox - for some reason this is not defined
5187                 pp.setWidth(Math.floor(value*progressEl.dom.firstChild.offsetWidth));
5188                 pp.setHeight(Math.floor(progressEl.dom.firstChild.offsetHeight));
5189             }
5190             return this;
5191         },        
5192
5193         /**
5194          * Returns true if the message box is currently displayed
5195          * @return {Boolean} True if the message box is visible, else false
5196          */
5197         isVisible : function(){
5198             return dlg && dlg.isVisible();  
5199         },
5200
5201         /**
5202          * Hides the message box if it is displayed
5203          */
5204         hide : function(){
5205             if(this.isVisible()){
5206                 dlg.hide();
5207             }  
5208         },
5209
5210         /**
5211          * Displays a new message box, or reinitializes an existing message box, based on the config options
5212          * passed in. All functions (e.g. prompt, alert, etc) on MessageBox call this function internally.
5213          * The following config object properties are supported:
5214          * <pre>
5215 Property    Type             Description
5216 ----------  ---------------  ------------------------------------------------------------------------------------
5217 animEl            String/Element   An id or Element from which the message box should animate as it opens and
5218                                    closes (defaults to undefined)
5219 buttons           Object/Boolean   A button config object (e.g., Roo.MessageBox.OKCANCEL or {ok:'Foo',
5220                                    cancel:'Bar'}), or false to not show any buttons (defaults to false)
5221 closable          Boolean          False to hide the top-right close button (defaults to true).  Note that
5222                                    progress and wait dialogs will ignore this property and always hide the
5223                                    close button as they can only be closed programmatically.
5224 cls               String           A custom CSS class to apply to the message box element
5225 defaultTextHeight Number           The default height in pixels of the message box's multiline textarea if
5226                                    displayed (defaults to 75)
5227 fn                Function         A callback function to execute after closing the dialog.  The arguments to the
5228                                    function will be btn (the name of the button that was clicked, if applicable,
5229                                    e.g. "ok"), and text (the value of the active text field, if applicable).
5230                                    Progress and wait dialogs will ignore this option since they do not respond to
5231                                    user actions and can only be closed programmatically, so any required function
5232                                    should be called by the same code after it closes the dialog.
5233 icon              String           A CSS class that provides a background image to be used as an icon for
5234                                    the dialog (e.g., Roo.MessageBox.WARNING or 'custom-class', defaults to '')
5235 maxWidth          Number           The maximum width in pixels of the message box (defaults to 600)
5236 minWidth          Number           The minimum width in pixels of the message box (defaults to 100)
5237 modal             Boolean          False to allow user interaction with the page while the message box is
5238                                    displayed (defaults to true)
5239 msg               String           A string that will replace the existing message box body text (defaults
5240                                    to the XHTML-compliant non-breaking space character '&#160;')
5241 multiline         Boolean          True to prompt the user to enter multi-line text (defaults to false)
5242 progress          Boolean          True to display a progress bar (defaults to false)
5243 progressText      String           The text to display inside the progress bar if progress = true (defaults to '')
5244 prompt            Boolean          True to prompt the user to enter single-line text (defaults to false)
5245 proxyDrag         Boolean          True to display a lightweight proxy while dragging (defaults to false)
5246 title             String           The title text
5247 value             String           The string value to set into the active textbox element if displayed
5248 wait              Boolean          True to display a progress bar (defaults to false)
5249 width             Number           The width of the dialog in pixels
5250 </pre>
5251          *
5252          * Example usage:
5253          * <pre><code>
5254 Roo.Msg.show({
5255    title: 'Address',
5256    msg: 'Please enter your address:',
5257    width: 300,
5258    buttons: Roo.MessageBox.OKCANCEL,
5259    multiline: true,
5260    fn: saveAddress,
5261    animEl: 'addAddressBtn'
5262 });
5263 </code></pre>
5264          * @param {Object} config Configuration options
5265          * @return {Roo.MessageBox} This message box
5266          */
5267         show : function(options)
5268         {
5269             
5270             // this causes nightmares if you show one dialog after another
5271             // especially on callbacks..
5272              
5273             if(this.isVisible()){
5274                 
5275                 this.hide();
5276                 Roo.log("[Roo.Messagebox] Show called while message displayed:" );
5277                 Roo.log("Old Dialog Message:" +  msgEl.innerHTML );
5278                 Roo.log("New Dialog Message:" +  options.msg )
5279                 //this.alert("ERROR", "Multiple dialogs where displayed at the same time");
5280                 //throw "Roo.MessageBox ERROR : Multiple dialogs where displayed at the same time";
5281                 
5282             }
5283             var d = this.getDialog();
5284             opt = options;
5285             d.setTitle(opt.title || "&#160;");
5286             d.closeEl.setDisplayed(opt.closable !== false);
5287             activeTextEl = textboxEl;
5288             opt.prompt = opt.prompt || (opt.multiline ? true : false);
5289             if(opt.prompt){
5290                 if(opt.multiline){
5291                     textboxEl.hide();
5292                     textareaEl.show();
5293                     textareaEl.setHeight(typeof opt.multiline == "number" ?
5294                         opt.multiline : this.defaultTextHeight);
5295                     activeTextEl = textareaEl;
5296                 }else{
5297                     textboxEl.show();
5298                     textareaEl.hide();
5299                 }
5300             }else{
5301                 textboxEl.hide();
5302                 textareaEl.hide();
5303             }
5304             progressEl.setDisplayed(opt.progress === true);
5305             if (opt.progress) {
5306                 d.animate = false; // do not animate progress, as it may not have finished animating before we close it..
5307             }
5308             this.updateProgress(0);
5309             activeTextEl.dom.value = opt.value || "";
5310             if(opt.prompt){
5311                 dlg.setDefaultButton(activeTextEl);
5312             }else{
5313                 var bs = opt.buttons;
5314                 var db = null;
5315                 if(bs && bs.ok){
5316                     db = buttons["ok"];
5317                 }else if(bs && bs.yes){
5318                     db = buttons["yes"];
5319                 }
5320                 dlg.setDefaultButton(db);
5321             }
5322             bwidth = updateButtons(opt.buttons);
5323             this.updateText(opt.msg);
5324             if(opt.cls){
5325                 d.el.addClass(opt.cls);
5326             }
5327             d.proxyDrag = opt.proxyDrag === true;
5328             d.modal = opt.modal !== false;
5329             d.mask = opt.modal !== false ? mask : false;
5330             if(!d.isVisible()){
5331                 // force it to the end of the z-index stack so it gets a cursor in FF
5332                 document.body.appendChild(dlg.el.dom);
5333                 d.animateTarget = null;
5334                 d.show(options.animEl);
5335             }
5336             return this;
5337         },
5338
5339         /**
5340          * Displays a message box with a progress bar.  This message box has no buttons and is not closeable by
5341          * the user.  You are responsible for updating the progress bar as needed via {@link Roo.MessageBox#updateProgress}
5342          * and closing the message box when the process is complete.
5343          * @param {String} title The title bar text
5344          * @param {String} msg The message box body text
5345          * @return {Roo.MessageBox} This message box
5346          */
5347         progress : function(title, msg){
5348             this.show({
5349                 title : title,
5350                 msg : msg,
5351                 buttons: false,
5352                 progress:true,
5353                 closable:false,
5354                 minWidth: this.minProgressWidth,
5355                 modal : true
5356             });
5357             return this;
5358         },
5359
5360         /**
5361          * Displays a standard read-only message box with an OK button (comparable to the basic JavaScript Window.alert).
5362          * If a callback function is passed it will be called after the user clicks the button, and the
5363          * id of the button that was clicked will be passed as the only parameter to the callback
5364          * (could also be the top-right close button).
5365          * @param {String} title The title bar text
5366          * @param {String} msg The message box body text
5367          * @param {Function} fn (optional) The callback function invoked after the message box is closed
5368          * @param {Object} scope (optional) The scope of the callback function
5369          * @return {Roo.MessageBox} This message box
5370          */
5371         alert : function(title, msg, fn, scope)
5372         {
5373             this.show({
5374                 title : title,
5375                 msg : msg,
5376                 buttons: this.OK,
5377                 fn: fn,
5378                 closable : false,
5379                 scope : scope,
5380                 modal : true
5381             });
5382             return this;
5383         },
5384
5385         /**
5386          * Displays a message box with an infinitely auto-updating progress bar.  This can be used to block user
5387          * interaction while waiting for a long-running process to complete that does not have defined intervals.
5388          * You are responsible for closing the message box when the process is complete.
5389          * @param {String} msg The message box body text
5390          * @param {String} title (optional) The title bar text
5391          * @return {Roo.MessageBox} This message box
5392          */
5393         wait : function(msg, title){
5394             this.show({
5395                 title : title,
5396                 msg : msg,
5397                 buttons: false,
5398                 closable:false,
5399                 progress:true,
5400                 modal:true,
5401                 width:300,
5402                 wait:true
5403             });
5404             waitTimer = Roo.TaskMgr.start({
5405                 run: function(i){
5406                     Roo.MessageBox.updateProgress(((((i+20)%20)+1)*5)*.01);
5407                 },
5408                 interval: 1000
5409             });
5410             return this;
5411         },
5412
5413         /**
5414          * Displays a confirmation message box with Yes and No buttons (comparable to JavaScript's Window.confirm).
5415          * If a callback function is passed it will be called after the user clicks either button, and the id of the
5416          * button that was clicked will be passed as the only parameter to the callback (could also be the top-right close button).
5417          * @param {String} title The title bar text
5418          * @param {String} msg The message box body text
5419          * @param {Function} fn (optional) The callback function invoked after the message box is closed
5420          * @param {Object} scope (optional) The scope of the callback function
5421          * @return {Roo.MessageBox} This message box
5422          */
5423         confirm : function(title, msg, fn, scope){
5424             this.show({
5425                 title : title,
5426                 msg : msg,
5427                 buttons: this.YESNO,
5428                 fn: fn,
5429                 scope : scope,
5430                 modal : true
5431             });
5432             return this;
5433         },
5434
5435         /**
5436          * Displays a message box with OK and Cancel buttons prompting the user to enter some text (comparable to
5437          * JavaScript's Window.prompt).  The prompt can be a single-line or multi-line textbox.  If a callback function
5438          * is passed it will be called after the user clicks either button, and the id of the button that was clicked
5439          * (could also be the top-right close button) and the text that was entered will be passed as the two
5440          * parameters to the callback.
5441          * @param {String} title The title bar text
5442          * @param {String} msg The message box body text
5443          * @param {Function} fn (optional) The callback function invoked after the message box is closed
5444          * @param {Object} scope (optional) The scope of the callback function
5445          * @param {Boolean/Number} multiline (optional) True to create a multiline textbox using the defaultTextHeight
5446          * property, or the height in pixels to create the textbox (defaults to false / single-line)
5447          * @return {Roo.MessageBox} This message box
5448          */
5449         prompt : function(title, msg, fn, scope, multiline){
5450             this.show({
5451                 title : title,
5452                 msg : msg,
5453                 buttons: this.OKCANCEL,
5454                 fn: fn,
5455                 minWidth:250,
5456                 scope : scope,
5457                 prompt:true,
5458                 multiline: multiline,
5459                 modal : true
5460             });
5461             return this;
5462         },
5463
5464         /**
5465          * Button config that displays a single OK button
5466          * @type Object
5467          */
5468         OK : {ok:true},
5469         /**
5470          * Button config that displays Yes and No buttons
5471          * @type Object
5472          */
5473         YESNO : {yes:true, no:true},
5474         /**
5475          * Button config that displays OK and Cancel buttons
5476          * @type Object
5477          */
5478         OKCANCEL : {ok:true, cancel:true},
5479         /**
5480          * Button config that displays Yes, No and Cancel buttons
5481          * @type Object
5482          */
5483         YESNOCANCEL : {yes:true, no:true, cancel:true},
5484
5485         /**
5486          * The default height in pixels of the message box's multiline textarea if displayed (defaults to 75)
5487          * @type Number
5488          */
5489         defaultTextHeight : 75,
5490         /**
5491          * The maximum width in pixels of the message box (defaults to 600)
5492          * @type Number
5493          */
5494         maxWidth : 600,
5495         /**
5496          * The minimum width in pixels of the message box (defaults to 100)
5497          * @type Number
5498          */
5499         minWidth : 100,
5500         /**
5501          * The minimum width in pixels of the message box if it is a progress-style dialog.  This is useful
5502          * for setting a different minimum width than text-only dialogs may need (defaults to 250)
5503          * @type Number
5504          */
5505         minProgressWidth : 250,
5506         /**
5507          * An object containing the default button text strings that can be overriden for localized language support.
5508          * Supported properties are: ok, cancel, yes and no.
5509          * Customize the default text like so: Roo.MessageBox.buttonText.yes = "S?";
5510          * @type Object
5511          */
5512         buttonText : {
5513             ok : "OK",
5514             cancel : "Cancel",
5515             yes : "Yes",
5516             no : "No"
5517         }
5518     };
5519 }();
5520
5521 /**
5522  * Shorthand for {@link Roo.MessageBox}
5523  */
5524 Roo.MessageBox = Roo.MessageBox || Roo.bootstrap.MessageBox;
5525 Roo.Msg = Roo.Msg || Roo.MessageBox;
5526 /*
5527  * - LGPL
5528  *
5529  * navbar
5530  * 
5531  */
5532
5533 /**
5534  * @class Roo.bootstrap.nav.Bar
5535  * @extends Roo.bootstrap.Component
5536  * @abstract
5537  * Bootstrap Navbar class
5538
5539  * @constructor
5540  * Create a new Navbar
5541  * @param {Object} config The config object
5542  */
5543
5544
5545 Roo.bootstrap.nav.Bar = function(config){
5546     Roo.bootstrap.nav.Bar.superclass.constructor.call(this, config);
5547     this.addEvents({
5548         // raw events
5549         /**
5550          * @event beforetoggle
5551          * Fire before toggle the menu
5552          * @param {Roo.EventObject} e
5553          */
5554         "beforetoggle" : true
5555     });
5556 };
5557
5558 Roo.extend(Roo.bootstrap.nav.Bar, Roo.bootstrap.Component,  {
5559     
5560     
5561    
5562     // private
5563     navItems : false,
5564     loadMask : false,
5565     
5566     
5567     getAutoCreate : function(){
5568         
5569         
5570         throw { message : "nav bar is now a abstract base class - use NavSimplebar / NavHeaderbar / NavSidebar etc..."};
5571         
5572     },
5573     
5574     initEvents :function ()
5575     {
5576         //Roo.log(this.el.select('.navbar-toggle',true));
5577         this.el.select('.navbar-toggle',true).on('click', this.onToggle , this);
5578         
5579         var mark = {
5580             tag: "div",
5581             cls:"x-dlg-mask"
5582         };
5583         
5584         this.maskEl = Roo.DomHelper.append(this.el, mark, true);
5585         
5586         var size = this.el.getSize();
5587         this.maskEl.setSize(size.width, size.height);
5588         this.maskEl.enableDisplayMode("block");
5589         this.maskEl.hide();
5590         
5591         if(this.loadMask){
5592             this.maskEl.show();
5593         }
5594     },
5595     
5596     
5597     getChildContainer : function()
5598     {
5599         if (this.el && this.el.select('.collapse').getCount()) {
5600             return this.el.select('.collapse',true).first();
5601         }
5602         
5603         return this.el;
5604     },
5605     
5606     mask : function()
5607     {
5608         this.maskEl.show();
5609     },
5610     
5611     unmask : function()
5612     {
5613         this.maskEl.hide();
5614     },
5615     onToggle : function()
5616     {
5617         
5618         if(this.fireEvent('beforetoggle', this) === false){
5619             return;
5620         }
5621         var ce = this.el.select('.navbar-collapse',true).first();
5622       
5623         if (!ce.hasClass('show')) {
5624            this.expand();
5625         } else {
5626             this.collapse();
5627         }
5628         
5629         
5630     
5631     },
5632     /**
5633      * Expand the navbar pulldown 
5634      */
5635     expand : function ()
5636     {
5637        
5638         var ce = this.el.select('.navbar-collapse',true).first();
5639         if (ce.hasClass('collapsing')) {
5640             return;
5641         }
5642         ce.dom.style.height = '';
5643                // show it...
5644         ce.addClass('in'); // old...
5645         ce.removeClass('collapse');
5646         ce.addClass('show');
5647         var h = ce.getHeight();
5648         Roo.log(h);
5649         ce.removeClass('show');
5650         // at this point we should be able to see it..
5651         ce.addClass('collapsing');
5652         
5653         ce.setHeight(0); // resize it ...
5654         ce.on('transitionend', function() {
5655             //Roo.log('done transition');
5656             ce.removeClass('collapsing');
5657             ce.addClass('show');
5658             ce.removeClass('collapse');
5659
5660             ce.dom.style.height = '';
5661         }, this, { single: true} );
5662         ce.setHeight(h);
5663         ce.dom.scrollTop = 0;
5664     },
5665     /**
5666      * Collapse the navbar pulldown 
5667      */
5668     collapse : function()
5669     {
5670          var ce = this.el.select('.navbar-collapse',true).first();
5671        
5672         if (ce.hasClass('collapsing') || ce.hasClass('collapse') ) {
5673             // it's collapsed or collapsing..
5674             return;
5675         }
5676         ce.removeClass('in'); // old...
5677         ce.setHeight(ce.getHeight());
5678         ce.removeClass('show');
5679         ce.addClass('collapsing');
5680         
5681         ce.on('transitionend', function() {
5682             ce.dom.style.height = '';
5683             ce.removeClass('collapsing');
5684             ce.addClass('collapse');
5685         }, this, { single: true} );
5686         ce.setHeight(0);
5687     }
5688     
5689     
5690     
5691 });
5692
5693
5694
5695  
5696
5697  /*
5698  * - LGPL
5699  *
5700  * navbar
5701  * 
5702  */
5703
5704 /**
5705  * @class Roo.bootstrap.nav.Simplebar
5706  * @extends Roo.bootstrap.nav.Bar
5707  * @children Roo.bootstrap.nav.Group Roo.bootstrap.Container Roo.bootstrap.form.Form Roo.bootstrap.Row Roo.bootstrap.Column Roo.bootstrap.Link
5708  * Bootstrap Sidebar class
5709  *
5710  * @cfg {Boolean} inverse is inverted color
5711  * 
5712  * @cfg {String} type (nav | pills | tabs)
5713  * @cfg {Boolean} arrangement stacked | justified
5714  * @cfg {String} align (left | right) alignment
5715  * 
5716  * @cfg {Boolean} main (true|false) main nav bar? default false
5717  * @cfg {Boolean} loadMask (true|false) loadMask on the bar
5718  * 
5719  * @cfg {String} tag (header|footer|nav|div) default is nav 
5720
5721  * @cfg {String} weight (light|primary|secondary|success|danger|warning|info|dark|white) default is light.
5722  * 
5723  * 
5724  * @constructor
5725  * Create a new Sidebar
5726  * @param {Object} config The config object
5727  */
5728
5729
5730 Roo.bootstrap.nav.Simplebar = function(config){
5731     Roo.bootstrap.nav.Simplebar.superclass.constructor.call(this, config);
5732 };
5733
5734 Roo.extend(Roo.bootstrap.nav.Simplebar, Roo.bootstrap.nav.Bar,  {
5735     
5736     inverse: false,
5737     
5738     type: false,
5739     arrangement: '',
5740     align : false,
5741     
5742     weight : 'light',
5743     
5744     main : false,
5745     
5746     
5747     tag : false,
5748     
5749     
5750     getAutoCreate : function(){
5751         
5752         
5753         var cfg = {
5754             tag : this.tag || 'div',
5755             cls : 'navbar roo-navbar-simple' //navbar-expand-lg ??
5756         };
5757         if (['light','white'].indexOf(this.weight) > -1) {
5758             cfg.cls += ['light','white'].indexOf(this.weight) > -1 ? ' navbar-light' : ' navbar-dark';
5759         }
5760         cfg.cls += ' bg-' + this.weight;
5761         
5762         if (this.inverse) {
5763             cfg.cls += ' navbar-inverse';
5764             
5765         }
5766         
5767         // i'm not actually sure these are really used - normally we add a navGroup to a navbar
5768         
5769         if (Roo.bootstrap.version == 4 && this.xtype == 'NavSimplebar') {
5770             return cfg;
5771         }
5772         
5773         
5774     
5775         
5776         cfg.cn = [
5777             {
5778                 cls: 'nav nav-' + this.xtype,
5779                 tag : 'ul'
5780             }
5781         ];
5782         
5783          
5784         this.type = this.type || 'nav';
5785         if (['tabs','pills'].indexOf(this.type) != -1) {
5786             cfg.cn[0].cls += ' nav-' + this.type
5787         
5788         
5789         } else {
5790             if (this.type!=='nav') {
5791                 Roo.log('nav type must be nav/tabs/pills')
5792             }
5793             cfg.cn[0].cls += ' navbar-nav'
5794         }
5795         
5796         
5797         
5798         
5799         if (['stacked','justified'].indexOf(this.arrangement) != -1) {
5800             cfg.cn[0].cls += ' nav-' + this.arrangement;
5801         }
5802         
5803         
5804         if (this.align === 'right') {
5805             cfg.cn[0].cls += ' navbar-right';
5806         }
5807         
5808         
5809         
5810         
5811         return cfg;
5812     
5813         
5814     }
5815     
5816     
5817     
5818 });
5819
5820
5821
5822  
5823
5824  
5825        /*
5826  * - LGPL
5827  *
5828  * navbar
5829  * navbar-fixed-top
5830  * navbar-expand-md  fixed-top 
5831  */
5832
5833 /**
5834  * @class Roo.bootstrap.nav.Headerbar
5835  * @extends Roo.bootstrap.nav.Simplebar
5836  * @children Roo.bootstrap.nav.Group Roo.bootstrap.Container Roo.bootstrap.form.Form Roo.bootstrap.Row Roo.bootstrap.Column Roo.bootstrap.Link
5837  * Bootstrap Sidebar class
5838  *
5839  * @cfg {String} brand what is brand
5840  * @cfg {String} position (fixed-top|fixed-bottom|static-top) position
5841  * @cfg {String} brand_href href of the brand
5842  * @cfg {Boolean} srButton generate the (screen reader / mobile) sr-only button   default true
5843  * @cfg {Boolean} autohide a top nav bar header that hides on scroll.
5844  * @cfg {Boolean} desktopCenter should the header be centered on desktop using a container class
5845  * @cfg {Roo.bootstrap.Row} mobilerow - a row to display on mobile only..
5846  * 
5847  * @constructor
5848  * Create a new Sidebar
5849  * @param {Object} config The config object
5850  */
5851
5852
5853 Roo.bootstrap.nav.Headerbar = function(config){
5854     Roo.bootstrap.nav.Headerbar.superclass.constructor.call(this, config);
5855       
5856 };
5857
5858 Roo.extend(Roo.bootstrap.nav.Headerbar, Roo.bootstrap.nav.Simplebar,  {
5859     
5860     position: '',
5861     brand: '',
5862     brand_href: false,
5863     srButton : true,
5864     autohide : false,
5865     desktopCenter : false,
5866    
5867     
5868     getAutoCreate : function(){
5869         
5870         var   cfg = {
5871             tag: this.nav || 'nav',
5872             cls: 'navbar navbar-expand-md',
5873             role: 'navigation',
5874             cn: []
5875         };
5876         
5877         var cn = cfg.cn;
5878         if (this.desktopCenter) {
5879             cn.push({cls : 'container', cn : []});
5880             cn = cn[0].cn;
5881         }
5882         
5883         if(this.srButton){
5884             var btn = {
5885                 tag: 'button',
5886                 type: 'button',
5887                 cls: 'navbar-toggle navbar-toggler',
5888                 'data-toggle': 'collapse',
5889                 cn: [
5890                     {
5891                         tag: 'span',
5892                         cls: 'sr-only',
5893                         html: 'Toggle navigation'
5894                     },
5895                     {
5896                         tag: 'span',
5897                         cls: 'icon-bar navbar-toggler-icon'
5898                     },
5899                     {
5900                         tag: 'span',
5901                         cls: 'icon-bar'
5902                     },
5903                     {
5904                         tag: 'span',
5905                         cls: 'icon-bar'
5906                     }
5907                 ]
5908             };
5909             
5910             cn.push( Roo.bootstrap.version == 4 ? btn : {
5911                 tag: 'div',
5912                 cls: 'navbar-header',
5913                 cn: [
5914                     btn
5915                 ]
5916             });
5917         }
5918         
5919         cn.push({
5920             tag: 'div',
5921             cls: Roo.bootstrap.version == 4  ? 'nav flex-row roo-navbar-collapse collapse navbar-collapse' : 'collapse navbar-collapse roo-navbar-collapse',
5922             cn : []
5923         });
5924         
5925         cfg.cls += this.inverse ? ' navbar-inverse navbar-dark bg-dark' : ' navbar-default';
5926         
5927         if (['light','white'].indexOf(this.weight) > -1) {
5928             cfg.cls += ['light','white'].indexOf(this.weight) > -1 ? ' navbar-light' : ' navbar-dark';
5929         }
5930         cfg.cls += ' bg-' + this.weight;
5931         
5932         
5933         if (['fixed-top','fixed-bottom','static-top'].indexOf(this.position)>-1) {
5934             cfg.cls += ' navbar-' + this.position + ' ' + this.position ;
5935             
5936             // tag can override this..
5937             
5938             cfg.tag = this.tag || (this.position  == 'fixed-bottom' ? 'footer' : 'header');
5939         }
5940         
5941         if (this.brand !== '') {
5942             var cp =  Roo.bootstrap.version == 4 ? cn : cn[0].cn;
5943             cp.unshift({ // changed from push ?? BS4 needs it at the start? - does this break or exsiting?
5944                 tag: 'a',
5945                 href: this.brand_href ? this.brand_href : '#',
5946                 cls: 'navbar-brand',
5947                 cn: [
5948                 this.brand
5949                 ]
5950             });
5951         }
5952         
5953         if(this.main){
5954             cfg.cls += ' main-nav';
5955         }
5956         
5957         
5958         return cfg;
5959
5960         
5961     },
5962     getHeaderChildContainer : function()
5963     {
5964         if (this.srButton && this.el.select('.navbar-header').getCount()) {
5965             return this.el.select('.navbar-header',true).first();
5966         }
5967         
5968         return this.getChildContainer();
5969     },
5970     
5971     getChildContainer : function()
5972     {
5973          
5974         return this.el.select('.roo-navbar-collapse',true).first();
5975          
5976         
5977     },
5978     
5979     initEvents : function()
5980     {
5981         Roo.bootstrap.nav.Headerbar.superclass.initEvents.call(this);
5982         
5983         if (this.autohide) {
5984             
5985             var prevScroll = 0;
5986             var ft = this.el;
5987             
5988             Roo.get(document).on('scroll',function(e) {
5989                 var ns = Roo.get(document).getScroll().top;
5990                 var os = prevScroll;
5991                 prevScroll = ns;
5992                 
5993                 if(ns > os){
5994                     ft.removeClass('slideDown');
5995                     ft.addClass('slideUp');
5996                     return;
5997                 }
5998                 ft.removeClass('slideUp');
5999                 ft.addClass('slideDown');
6000                  
6001               
6002           },this);
6003         }
6004     }    
6005     
6006 });
6007
6008
6009
6010  
6011
6012  /*
6013  * - LGPL
6014  *
6015  * navbar
6016  * 
6017  */
6018
6019 /**
6020  * @class Roo.bootstrap.nav.Sidebar
6021  * @extends Roo.bootstrap.nav.Bar
6022  * @children Roo.bootstrap.nav.Group Roo.bootstrap.Container Roo.bootstrap.form.Form Roo.bootstrap.Row Roo.bootstrap.Column Roo.bootstrap.Link
6023  * Bootstrap Sidebar class
6024  * 
6025  * @constructor
6026  * Create a new Sidebar
6027  * @param {Object} config The config object
6028  */
6029
6030
6031 Roo.bootstrap.nav.Sidebar = function(config){
6032     Roo.bootstrap.nav.Sidebar.superclass.constructor.call(this, config);
6033 };
6034
6035 Roo.extend(Roo.bootstrap.nav.Sidebar, Roo.bootstrap.nav.Bar,  {
6036     
6037     sidebar : true, // used by Navbar Item and NavbarGroup at present...
6038     
6039     getAutoCreate : function(){
6040         
6041         
6042         return  {
6043             tag: 'div',
6044             cls: 'sidebar sidebar-nav'
6045         };
6046     
6047         
6048     }
6049     
6050     
6051     
6052 });
6053
6054
6055
6056  
6057
6058  /*
6059  * - LGPL
6060  *
6061  * nav group
6062  * 
6063  */
6064
6065 /**
6066  * @class Roo.bootstrap.nav.Group
6067  * @extends Roo.bootstrap.Component
6068  * @children Roo.bootstrap.nav.Item
6069  * Bootstrap NavGroup class
6070  * @cfg {String} align (left|right)
6071  * @cfg {Boolean} inverse
6072  * @cfg {String} type (nav|pills|tab) default nav
6073  * @cfg {String} navId - reference Id for navbar.
6074  * @cfg {Boolean} pilltype default true (turn to off to disable active toggle)
6075  * 
6076  * @constructor
6077  * Create a new nav group
6078  * @param {Object} config The config object
6079  */
6080
6081 Roo.bootstrap.nav.Group = function(config){
6082     Roo.bootstrap.nav.Group.superclass.constructor.call(this, config);
6083     this.navItems = [];
6084    
6085     Roo.bootstrap.nav.Group.register(this);
6086      this.addEvents({
6087         /**
6088              * @event changed
6089              * Fires when the active item changes
6090              * @param {Roo.bootstrap.nav.Group} this
6091              * @param {Roo.bootstrap.Navbar.Item} selected The item selected
6092              * @param {Roo.bootstrap.Navbar.Item} prev The previously selected item 
6093          */
6094         'changed': true
6095      });
6096     
6097 };
6098
6099 Roo.extend(Roo.bootstrap.nav.Group, Roo.bootstrap.Component,  {
6100     
6101     align: '',
6102     inverse: false,
6103     form: false,
6104     type: 'nav',
6105     navId : '',
6106     // private
6107     pilltype : true,
6108     
6109     navItems : false, 
6110     
6111     getAutoCreate : function()
6112     {
6113         var cfg = Roo.apply({}, Roo.bootstrap.nav.Group.superclass.getAutoCreate.call(this));
6114         
6115         cfg = {
6116             tag : 'ul',
6117             cls: 'nav' 
6118         };
6119         if (Roo.bootstrap.version == 4) {
6120             if (['tabs','pills'].indexOf(this.type) != -1) {
6121                 cfg.cls += ' nav-' + this.type; 
6122             } else {
6123                 // trying to remove so header bar can right align top?
6124                 if (this.parent() && this.parent().xtype != 'NavHeaderbar') {
6125                     // do not use on header bar... 
6126                     cfg.cls += ' navbar-nav';
6127                 }
6128             }
6129             
6130         } else {
6131             if (['tabs','pills'].indexOf(this.type) != -1) {
6132                 cfg.cls += ' nav-' + this.type
6133             } else {
6134                 if (this.type !== 'nav') {
6135                     Roo.log('nav type must be nav/tabs/pills')
6136                 }
6137                 cfg.cls += ' navbar-nav'
6138             }
6139         }
6140         
6141         if (this.parent() && this.parent().sidebar) {
6142             cfg = {
6143                 tag: 'ul',
6144                 cls: 'dashboard-menu sidebar-menu'
6145             };
6146             
6147             return cfg;
6148         }
6149         
6150         if (this.form === true) {
6151             cfg = {
6152                 tag: 'form',
6153                 cls: 'navbar-form form-inline'
6154             };
6155             //nav navbar-right ml-md-auto
6156             if (this.align === 'right') {
6157                 cfg.cls += ' navbar-right ml-md-auto';
6158             } else {
6159                 cfg.cls += ' navbar-left';
6160             }
6161         }
6162         
6163         if (this.align === 'right') {
6164             cfg.cls += ' navbar-right ml-md-auto';
6165         } else {
6166             cfg.cls += ' mr-auto';
6167         }
6168         
6169         if (this.inverse) {
6170             cfg.cls += ' navbar-inverse';
6171             
6172         }
6173         
6174         
6175         return cfg;
6176     },
6177     /**
6178     * sets the active Navigation item
6179     * @param {Roo.bootstrap.nav.Item} the new current navitem
6180     */
6181     setActiveItem : function(item)
6182     {
6183         var prev = false;
6184         Roo.each(this.navItems, function(v){
6185             if (v == item) {
6186                 return ;
6187             }
6188             if (v.isActive()) {
6189                 v.setActive(false, true);
6190                 prev = v;
6191                 
6192             }
6193             
6194         });
6195
6196         item.setActive(true, true);
6197         this.fireEvent('changed', this, item, prev);
6198         
6199         
6200     },
6201     /**
6202     * gets the active Navigation item
6203     * @return {Roo.bootstrap.nav.Item} the current navitem
6204     */
6205     getActive : function()
6206     {
6207         
6208         var prev = false;
6209         Roo.each(this.navItems, function(v){
6210             
6211             if (v.isActive()) {
6212                 prev = v;
6213                 
6214             }
6215             
6216         });
6217         return prev;
6218     },
6219     
6220     indexOfNav : function()
6221     {
6222         
6223         var prev = false;
6224         Roo.each(this.navItems, function(v,i){
6225             
6226             if (v.isActive()) {
6227                 prev = i;
6228                 
6229             }
6230             
6231         });
6232         return prev;
6233     },
6234     /**
6235     * adds a Navigation item
6236     * @param {Roo.bootstrap.nav.Item} the navitem to add
6237     */
6238     addItem : function(cfg)
6239     {
6240         if (this.form && Roo.bootstrap.version == 4) {
6241             cfg.tag = 'div';
6242         }
6243         var cn = new Roo.bootstrap.nav.Item(cfg);
6244         this.register(cn);
6245         cn.parentId = this.id;
6246         cn.onRender(this.el, null);
6247         return cn;
6248     },
6249     /**
6250     * register a Navigation item
6251     * @param {Roo.bootstrap.nav.Item} the navitem to add
6252     */
6253     register : function(item)
6254     {
6255         this.navItems.push( item);
6256         item.navId = this.navId;
6257     
6258     },
6259     
6260     /**
6261     * clear all the Navigation item
6262     */
6263    
6264     clearAll : function()
6265     {
6266         this.navItems = [];
6267         this.el.dom.innerHTML = '';
6268     },
6269     
6270     getNavItem: function(tabId)
6271     {
6272         var ret = false;
6273         Roo.each(this.navItems, function(e) {
6274             if (e.tabId == tabId) {
6275                ret =  e;
6276                return false;
6277             }
6278             return true;
6279             
6280         });
6281         return ret;
6282     },
6283     
6284     setActiveNext : function()
6285     {
6286         var i = this.indexOfNav(this.getActive());
6287         if (i > this.navItems.length) {
6288             return;
6289         }
6290         this.setActiveItem(this.navItems[i+1]);
6291     },
6292     setActivePrev : function()
6293     {
6294         var i = this.indexOfNav(this.getActive());
6295         if (i  < 1) {
6296             return;
6297         }
6298         this.setActiveItem(this.navItems[i-1]);
6299     },
6300     clearWasActive : function(except) {
6301         Roo.each(this.navItems, function(e) {
6302             if (e.tabId != except.tabId && e.was_active) {
6303                e.was_active = false;
6304                return false;
6305             }
6306             return true;
6307             
6308         });
6309     },
6310     getWasActive : function ()
6311     {
6312         var r = false;
6313         Roo.each(this.navItems, function(e) {
6314             if (e.was_active) {
6315                r = e;
6316                return false;
6317             }
6318             return true;
6319             
6320         });
6321         return r;
6322     }
6323     
6324     
6325 });
6326
6327  
6328 Roo.apply(Roo.bootstrap.nav.Group, {
6329     
6330     groups: {},
6331      /**
6332     * register a Navigation Group
6333     * @param {Roo.bootstrap.nav.Group} the navgroup to add
6334     */
6335     register : function(navgrp)
6336     {
6337         this.groups[navgrp.navId] = navgrp;
6338         
6339     },
6340     /**
6341     * fetch a Navigation Group based on the navigation ID
6342     * @param {string} the navgroup to add
6343     * @returns {Roo.bootstrap.nav.Group} the navgroup 
6344     */
6345     get: function(navId) {
6346         if (typeof(this.groups[navId]) == 'undefined') {
6347             return false;
6348             //this.register(new Roo.bootstrap.nav.Group({ navId : navId }));
6349         }
6350         return this.groups[navId] ;
6351     }
6352     
6353     
6354     
6355 });
6356
6357  /**
6358  * @class Roo.bootstrap.nav.Item
6359  * @extends Roo.bootstrap.Component
6360  * @children Roo.bootstrap.Container Roo.bootstrap.Button
6361  * @parent Roo.bootstrap.nav.Group
6362  * @licence LGPL
6363  * Bootstrap Navbar.NavItem class
6364  * 
6365  * @cfg {String} href  link to
6366  * @cfg {String} button_weight (default|primary|secondary|success|info|warning|danger|link|light|dark) default none
6367  * @cfg {Boolean} button_outline show and outlined button
6368  * @cfg {String} html content of button
6369  * @cfg {String} badge text inside badge
6370  * @cfg {String} badgecls (bg-green|bg-red|bg-yellow)the extra classes for the badge
6371  * @cfg {String} glyphicon DEPRICATED - use fa
6372  * @cfg {String} icon DEPRICATED - use fa
6373  * @cfg {String} fa - Fontawsome icon name (can add stuff to it like fa-2x)
6374  * @cfg {Boolean} active Is item active
6375  * @cfg {Boolean} disabled Is item disabled
6376  * @cfg {String} linkcls  Link Class
6377  * @cfg {Boolean} preventDefault (true | false) default false
6378  * @cfg {String} tabId the tab that this item activates.
6379  * @cfg {String} tagtype (a|span) render as a href or span?
6380  * @cfg {Boolean} animateRef (true|false) link to element default false  
6381  * @cfg {Roo.bootstrap.menu.Menu} menu a Menu 
6382   
6383  * @constructor
6384  * Create a new Navbar Item
6385  * @param {Object} config The config object
6386  */
6387 Roo.bootstrap.nav.Item = function(config){
6388     Roo.bootstrap.nav.Item.superclass.constructor.call(this, config);
6389     this.addEvents({
6390         // raw events
6391         /**
6392          * @event click
6393          * The raw click event for the entire grid.
6394          * @param {Roo.EventObject} e
6395          */
6396         "click" : true,
6397          /**
6398             * @event changed
6399             * Fires when the active item active state changes
6400             * @param {Roo.bootstrap.nav.Item} this
6401             * @param {boolean} state the new state
6402              
6403          */
6404         'changed': true,
6405         /**
6406             * @event scrollto
6407             * Fires when scroll to element
6408             * @param {Roo.bootstrap.nav.Item} this
6409             * @param {Object} options
6410             * @param {Roo.EventObject} e
6411              
6412          */
6413         'scrollto': true
6414     });
6415    
6416 };
6417
6418 Roo.extend(Roo.bootstrap.nav.Item, Roo.bootstrap.Component,  {
6419     
6420     href: false,
6421     html: '',
6422     badge: '',
6423     icon: false,
6424     fa : false,
6425     glyphicon: false,
6426     active: false,
6427     preventDefault : false,
6428     tabId : false,
6429     tagtype : 'a',
6430     tag: 'li',
6431     disabled : false,
6432     animateRef : false,
6433     was_active : false,
6434     button_weight : '',
6435     button_outline : false,
6436     linkcls : '',
6437     navLink: false,
6438     
6439     getAutoCreate : function(){
6440          
6441         var cfg = {
6442             tag: this.tag,
6443             cls: 'nav-item'
6444         };
6445         
6446         cfg.cls =  typeof(cfg.cls) == 'undefined'  ? '' : cfg.cls;
6447         
6448         if (this.active) {
6449             cfg.cls +=  ' active' ;
6450         }
6451         if (this.disabled) {
6452             cfg.cls += ' disabled';
6453         }
6454         
6455         // BS4 only?
6456         if (this.button_weight.length) {
6457             cfg.tag = this.href ? 'a' : 'button';
6458             cfg.html = this.html || '';
6459             cfg.cls += ' btn btn' + (this.button_outline ? '-outline' : '') + '-' + this.button_weight;
6460             if (this.href) {
6461                 cfg.href = this.href;
6462             }
6463             if (this.fa) {
6464                 cfg.html = '<i class="fa fas fa-'+this.fa+'"></i> <span class="nav-html">' + this.html + '</span>';
6465             } else {
6466                 cfg.cls += " nav-html";
6467             }
6468             
6469             // menu .. should add dropdown-menu class - so no need for carat..
6470             
6471             if (this.badge !== '') {
6472                  
6473                 cfg.html += ' <span class="badge badge-secondary">' + this.badge + '</span>';
6474             }
6475             return cfg;
6476         }
6477         
6478         if (this.href || this.html || this.glyphicon || this.icon || this.fa) {
6479             cfg.cn = [
6480                 {
6481                     tag: this.tagtype,
6482                     href : this.href || "#",
6483                     html: this.html || '',
6484                     cls : ''
6485                 }
6486             ];
6487             if (this.tagtype == 'a') {
6488                 cfg.cn[0].cls = 'nav-link' +  (this.active ?  ' active'  : '') + ' ' + this.linkcls;
6489         
6490             }
6491             if (this.icon) {
6492                 cfg.cn[0].html = '<i class="'+this.icon+'"></i> <span class="nav-html">' + cfg.cn[0].html + '</span>';
6493             } else  if (this.fa) {
6494                 cfg.cn[0].html = '<i class="fa fas fa-'+this.fa+'"></i> <span class="nav-html">' + cfg.cn[0].html + '</span>';
6495             } else if(this.glyphicon) {
6496                 cfg.cn[0].html = '<span class="glyphicon glyphicon-' + this.glyphicon + '"></span> '  + cfg.cn[0].html;
6497             } else {
6498                 cfg.cn[0].cls += " nav-html";
6499             }
6500             
6501             if (this.menu) {
6502                 cfg.cn[0].html += " <span class='caret'></span>";
6503              
6504             }
6505             
6506             if (this.badge !== '') {
6507                 cfg.cn[0].html += ' <span class="badge badge-secondary">' + this.badge + '</span>';
6508             }
6509         }
6510         
6511         
6512         
6513         return cfg;
6514     },
6515     onRender : function(ct, position)
6516     {
6517        // Roo.log("Call onRender: " + this.xtype);
6518         if (Roo.bootstrap.version == 4 && ct.dom.type != 'ul') {
6519             this.tag = 'div';
6520         }
6521         
6522         var ret = Roo.bootstrap.nav.Item.superclass.onRender.call(this, ct, position);
6523         this.navLink = this.el.select('.nav-link',true).first();
6524         this.htmlEl = this.el.hasClass('nav-html') ? this.el : this.el.select('.nav-html',true).first();
6525         return ret;
6526     },
6527       
6528     
6529     initEvents: function() 
6530     {
6531         if (typeof (this.menu) != 'undefined') {
6532             this.menu.parentType = this.xtype;
6533             this.menu.triggerEl = this.el;
6534             this.menu = this.addxtype(Roo.apply({}, this.menu));
6535         }
6536         
6537         this.el.on('click', this.onClick, this);
6538         
6539         //if(this.tagtype == 'span'){
6540         //    this.el.select('span',true).on('click', this.onClick, this);
6541         //}
6542        
6543         // at this point parent should be available..
6544         this.parent().register(this);
6545     },
6546     
6547     onClick : function(e)
6548     {
6549         if (e.getTarget('.dropdown-menu-item')) {
6550             // did you click on a menu itemm.... - then don't trigger onclick..
6551             return;
6552         }
6553         
6554         if(
6555                 this.preventDefault ||
6556                                 this.href === false ||
6557                 this.href === '#' 
6558         ){
6559             //Roo.log("NavItem - prevent Default?");
6560             e.preventDefault();
6561         }
6562         
6563         if (this.disabled) {
6564             return;
6565         }
6566         
6567         var tg = Roo.bootstrap.TabGroup.get(this.navId);
6568         if (tg && tg.transition) {
6569             Roo.log("waiting for the transitionend");
6570             return;
6571         }
6572         
6573         
6574         
6575         //Roo.log("fire event clicked");
6576         if(this.fireEvent('click', this, e) === false){
6577             return;
6578         };
6579         
6580         if(this.tagtype == 'span'){
6581             return;
6582         }
6583         
6584         //Roo.log(this.href);
6585         var ael = this.el.select('a',true).first();
6586         //Roo.log(ael);
6587         
6588         if(ael && this.animateRef && this.href.indexOf('#') > -1){
6589             //Roo.log(["test:",ael.dom.href.split("#")[0], document.location.toString().split("#")[0]]);
6590             if (ael.dom.href.split("#")[0] != document.location.toString().split("#")[0]) {
6591                 return; // ignore... - it's a 'hash' to another page.
6592             }
6593             Roo.log("NavItem - prevent Default?");
6594             e.preventDefault();
6595             this.scrollToElement(e);
6596         }
6597         
6598         
6599         var p =  this.parent();
6600    
6601         if (['tabs','pills'].indexOf(p.type)!==-1 && p.pilltype) {
6602             if (typeof(p.setActiveItem) !== 'undefined') {
6603                 p.setActiveItem(this);
6604             }
6605         }
6606         
6607         // if parent is a navbarheader....- and link is probably a '#' page ref.. then remove the expanded menu.
6608         if (p.parentType == 'NavHeaderbar' && !this.menu) {
6609             // remove the collapsed menu expand...
6610             p.parent().el.select('.roo-navbar-collapse',true).removeClass('in');  
6611         }
6612     },
6613     
6614     isActive: function () {
6615         return this.active
6616     },
6617     setActive : function(state, fire, is_was_active)
6618     {
6619         if (this.active && !state && this.navId) {
6620             this.was_active = true;
6621             var nv = Roo.bootstrap.nav.Group.get(this.navId);
6622             if (nv) {
6623                 nv.clearWasActive(this);
6624             }
6625             
6626         }
6627         this.active = state;
6628         
6629         if (!state ) {
6630             this.el.removeClass('active');
6631             this.navLink ? this.navLink.removeClass('active') : false;
6632         } else if (!this.el.hasClass('active')) {
6633             
6634             this.el.addClass('active');
6635             if (Roo.bootstrap.version == 4 && this.navLink ) {
6636                 this.navLink.addClass('active');
6637             }
6638             
6639         }
6640         if (fire) {
6641             this.fireEvent('changed', this, state);
6642         }
6643         
6644         // show a panel if it's registered and related..
6645         
6646         if (!this.navId || !this.tabId || !state || is_was_active) {
6647             return;
6648         }
6649         
6650         var tg = Roo.bootstrap.TabGroup.get(this.navId);
6651         if (!tg) {
6652             return;
6653         }
6654         var pan = tg.getPanelByName(this.tabId);
6655         if (!pan) {
6656             return;
6657         }
6658         // if we can not flip to new panel - go back to old nav highlight..
6659         if (false == tg.showPanel(pan)) {
6660             var nv = Roo.bootstrap.nav.Group.get(this.navId);
6661             if (nv) {
6662                 var onav = nv.getWasActive();
6663                 if (onav) {
6664                     onav.setActive(true, false, true);
6665                 }
6666             }
6667             
6668         }
6669         
6670         
6671         
6672     },
6673      // this should not be here...
6674     setDisabled : function(state)
6675     {
6676         this.disabled = state;
6677         if (!state ) {
6678             this.el.removeClass('disabled');
6679         } else if (!this.el.hasClass('disabled')) {
6680             this.el.addClass('disabled');
6681         }
6682         
6683     },
6684     
6685     /**
6686      * Fetch the element to display the tooltip on.
6687      * @return {Roo.Element} defaults to this.el
6688      */
6689     tooltipEl : function()
6690     {
6691         return this.el; //this.tagtype  == 'a' ? this.el  : this.el.select('' + this.tagtype + '', true).first();
6692     },
6693     
6694     scrollToElement : function(e)
6695     {
6696         var c = document.body;
6697         
6698         /*
6699          * Firefox / IE places the overflow at the html level, unless specifically styled to behave differently.
6700          */
6701         if(Roo.isFirefox || Roo.isIE || Roo.isIE11){
6702             c = document.documentElement;
6703         }
6704         
6705         var target = Roo.get(c).select('a[name=' + this.href.split('#')[1] +']', true).first();
6706         
6707         if(!target){
6708             return;
6709         }
6710
6711         var o = target.calcOffsetsTo(c);
6712         
6713         var options = {
6714             target : target,
6715             value : o[1]
6716         };
6717         
6718         this.fireEvent('scrollto', this, options, e);
6719         
6720         Roo.get(c).scrollTo('top', options.value, true);
6721         
6722         return;
6723     },
6724     /**
6725      * Set the HTML (text content) of the item
6726      * @param {string} html  content for the nav item
6727      */
6728     setHtml : function(html)
6729     {
6730         this.html = html;
6731         this.htmlEl.dom.innerHTML = html;
6732         
6733     } 
6734 });
6735  
6736
6737  /*
6738  * - LGPL
6739  *
6740  * sidebar item
6741  *
6742  *  li
6743  *    <span> icon </span>
6744  *    <span> text </span>
6745  *    <span>badge </span>
6746  */
6747
6748 /**
6749  * @class Roo.bootstrap.nav.SidebarItem
6750  * @extends Roo.bootstrap.nav.Item
6751  * Bootstrap Navbar.NavSidebarItem class
6752  * 
6753  * {String} badgeWeight (default|primary|success|info|warning|danger)the extra classes for the badge
6754  * {Boolean} open is the menu open
6755  * {Boolean} buttonView use button as the tigger el rather that a (default false)
6756  * {String} buttonWeight (default|primary|success|info|warning|danger)the extra classes for the button
6757  * {String} buttonSize (sm|md|lg)the extra classes for the button
6758  * {Boolean} showArrow show arrow next to the text (default true)
6759  * @constructor
6760  * Create a new Navbar Button
6761  * @param {Object} config The config object
6762  */
6763 Roo.bootstrap.nav.SidebarItem = function(config){
6764     Roo.bootstrap.nav.SidebarItem.superclass.constructor.call(this, config);
6765     this.addEvents({
6766         // raw events
6767         /**
6768          * @event click
6769          * The raw click event for the entire grid.
6770          * @param {Roo.EventObject} e
6771          */
6772         "click" : true,
6773          /**
6774             * @event changed
6775             * Fires when the active item active state changes
6776             * @param {Roo.bootstrap.nav.SidebarItem} this
6777             * @param {boolean} state the new state
6778              
6779          */
6780         'changed': true
6781     });
6782    
6783 };
6784
6785 Roo.extend(Roo.bootstrap.nav.SidebarItem, Roo.bootstrap.nav.Item,  {
6786     
6787     badgeWeight : 'default',
6788     
6789     open: false,
6790     
6791     buttonView : false,
6792     
6793     buttonWeight : 'default',
6794     
6795     buttonSize : 'md',
6796     
6797     showArrow : true,
6798     
6799     getAutoCreate : function(){
6800         
6801         
6802         var a = {
6803                 tag: 'a',
6804                 href : this.href || '#',
6805                 cls: '',
6806                 html : '',
6807                 cn : []
6808         };
6809         
6810         if(this.buttonView){
6811             a = {
6812                 tag: 'button',
6813                 href : this.href || '#',
6814                 cls: 'btn btn-' + this.buttonWeight + ' btn-' + this.buttonSize + 'roo-button-dropdown-toggle',
6815                 html : this.html,
6816                 cn : []
6817             };
6818         }
6819         
6820         var cfg = {
6821             tag: 'li',
6822             cls: '',
6823             cn: [ a ]
6824         };
6825         
6826         if (this.active) {
6827             cfg.cls += ' active';
6828         }
6829         
6830         if (this.disabled) {
6831             cfg.cls += ' disabled';
6832         }
6833         if (this.open) {
6834             cfg.cls += ' open x-open';
6835         }
6836         // left icon..
6837         if (this.glyphicon || this.icon) {
6838             var c = this.glyphicon  ? ('glyphicon glyphicon-'+this.glyphicon)  : this.icon;
6839             a.cn.push({ tag : 'i', cls : c }) ;
6840         }
6841         
6842         if(!this.buttonView){
6843             var span = {
6844                 tag: 'span',
6845                 html : this.html || ''
6846             };
6847
6848             a.cn.push(span);
6849             
6850         }
6851         
6852         if (this.badge !== '') {
6853             a.cn.push({ tag: 'span',  cls : 'badge pull-right badge-' + this.badgeWeight, html: this.badge }); 
6854         }
6855         
6856         if (this.menu) {
6857             
6858             if(this.showArrow){
6859                 a.cn.push({ tag : 'i', cls : 'glyphicon glyphicon-chevron-down pull-right'});
6860             }
6861             
6862             a.cls += ' dropdown-toggle treeview' ;
6863         }
6864         
6865         return cfg;
6866     },
6867     
6868     initEvents : function()
6869     { 
6870         if (typeof (this.menu) != 'undefined') {
6871             this.menu.parentType = this.xtype;
6872             this.menu.triggerEl = this.el;
6873             this.menu = this.addxtype(Roo.apply({}, this.menu));
6874         }
6875         
6876         this.el.on('click', this.onClick, this);
6877         
6878         if(this.badge !== ''){
6879             this.badgeEl = this.el.select('.badge', true).first().setVisibilityMode(Roo.Element.DISPLAY);
6880         }
6881         
6882     },
6883     
6884     onClick : function(e)
6885     {
6886         if(this.disabled){
6887             e.preventDefault();
6888             return;
6889         }
6890         
6891         if(this.preventDefault){
6892             e.preventDefault();
6893         }
6894         
6895         this.fireEvent('click', this, e);
6896     },
6897     
6898     disable : function()
6899     {
6900         this.setDisabled(true);
6901     },
6902     
6903     enable : function()
6904     {
6905         this.setDisabled(false);
6906     },
6907     
6908     setDisabled : function(state)
6909     {
6910         if(this.disabled == state){
6911             return;
6912         }
6913         
6914         this.disabled = state;
6915         
6916         if (state) {
6917             this.el.addClass('disabled');
6918             return;
6919         }
6920         
6921         this.el.removeClass('disabled');
6922         
6923         return;
6924     },
6925     
6926     setActive : function(state)
6927     {
6928         if(this.active == state){
6929             return;
6930         }
6931         
6932         this.active = state;
6933         
6934         if (state) {
6935             this.el.addClass('active');
6936             return;
6937         }
6938         
6939         this.el.removeClass('active');
6940         
6941         return;
6942     },
6943     
6944     isActive: function () 
6945     {
6946         return this.active;
6947     },
6948     
6949     setBadge : function(str)
6950     {
6951         if(!this.badgeEl){
6952             return;
6953         }
6954         
6955         this.badgeEl.dom.innerHTML = str;
6956     }
6957     
6958    
6959      
6960  
6961 });
6962  
6963
6964  /*
6965  * - LGPL
6966  *
6967  * nav progress bar
6968  * 
6969  */
6970
6971 /**
6972  * @class Roo.bootstrap.nav.ProgressBar
6973  * @extends Roo.bootstrap.Component
6974  * @children Roo.bootstrap.nav.ProgressBarItem
6975  * Bootstrap NavProgressBar class
6976  * 
6977  * @constructor
6978  * Create a new nav progress bar - a bar indicating step along a process
6979  * @param {Object} config The config object
6980  */
6981
6982 Roo.bootstrap.nav.ProgressBar = function(config){
6983     Roo.bootstrap.nav.ProgressBar.superclass.constructor.call(this, config);
6984
6985     this.bullets = this.bullets || [];
6986    
6987 //    Roo.bootstrap.nav.ProgressBar.register(this);
6988      this.addEvents({
6989         /**
6990              * @event changed
6991              * Fires when the active item changes
6992              * @param {Roo.bootstrap.nav.ProgressBar} this
6993              * @param {Roo.bootstrap.nav.ProgressItem} selected The item selected
6994              * @param {Roo.bootstrap.nav.ProgressItem} prev The previously selected item 
6995          */
6996         'changed': true
6997      });
6998     
6999 };
7000
7001 Roo.extend(Roo.bootstrap.nav.ProgressBar, Roo.bootstrap.Component,  {
7002     /**
7003      * @cfg {Roo.bootstrap.nav.ProgressItem} NavProgressBar:bullets[]
7004      * Bullets for the Nav Progress bar for the toolbar
7005      */
7006     bullets : [],
7007     barItems : [],
7008     
7009     getAutoCreate : function()
7010     {
7011         var cfg = Roo.apply({}, Roo.bootstrap.nav.ProgressBar.superclass.getAutoCreate.call(this));
7012         
7013         cfg = {
7014             tag : 'div',
7015             cls : 'roo-navigation-bar-group',
7016             cn : [
7017                 {
7018                     tag : 'div',
7019                     cls : 'roo-navigation-top-bar'
7020                 },
7021                 {
7022                     tag : 'div',
7023                     cls : 'roo-navigation-bullets-bar',
7024                     cn : [
7025                         {
7026                             tag : 'ul',
7027                             cls : 'roo-navigation-bar'
7028                         }
7029                     ]
7030                 },
7031                 
7032                 {
7033                     tag : 'div',
7034                     cls : 'roo-navigation-bottom-bar'
7035                 }
7036             ]
7037             
7038         };
7039         
7040         return cfg;
7041         
7042     },
7043     
7044     initEvents: function() 
7045     {
7046         
7047     },
7048     
7049     onRender : function(ct, position) 
7050     {
7051         Roo.bootstrap.nav.ProgressBar.superclass.onRender.call(this, ct, position);
7052         
7053         if(this.bullets.length){
7054             Roo.each(this.bullets, function(b){
7055                this.addItem(b);
7056             }, this);
7057         }
7058         
7059         this.format();
7060         
7061     },
7062     
7063     addItem : function(cfg)
7064     {
7065         var item = new Roo.bootstrap.nav.ProgressItem(cfg);
7066         
7067         item.parentId = this.id;
7068         item.render(this.el.select('.roo-navigation-bar', true).first(), null);
7069         
7070         if(cfg.html){
7071             var top = new Roo.bootstrap.Element({
7072                 tag : 'div',
7073                 cls : 'roo-navigation-bar-text'
7074             });
7075             
7076             var bottom = new Roo.bootstrap.Element({
7077                 tag : 'div',
7078                 cls : 'roo-navigation-bar-text'
7079             });
7080             
7081             top.onRender(this.el.select('.roo-navigation-top-bar', true).first(), null);
7082             bottom.onRender(this.el.select('.roo-navigation-bottom-bar', true).first(), null);
7083             
7084             var topText = new Roo.bootstrap.Element({
7085                 tag : 'span',
7086                 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? cfg.html : ''
7087             });
7088             
7089             var bottomText = new Roo.bootstrap.Element({
7090                 tag : 'span',
7091                 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? '' : cfg.html
7092             });
7093             
7094             topText.onRender(top.el, null);
7095             bottomText.onRender(bottom.el, null);
7096             
7097             item.topEl = top;
7098             item.bottomEl = bottom;
7099         }
7100         
7101         this.barItems.push(item);
7102         
7103         return item;
7104     },
7105     
7106     getActive : function()
7107     {
7108         var active = false;
7109         
7110         Roo.each(this.barItems, function(v){
7111             
7112             if (!v.isActive()) {
7113                 return;
7114             }
7115             
7116             active = v;
7117             return false;
7118             
7119         });
7120         
7121         return active;
7122     },
7123     
7124     setActiveItem : function(item)
7125     {
7126         var prev = false;
7127         
7128         Roo.each(this.barItems, function(v){
7129             if (v.rid == item.rid) {
7130                 return ;
7131             }
7132             
7133             if (v.isActive()) {
7134                 v.setActive(false);
7135                 prev = v;
7136             }
7137         });
7138
7139         item.setActive(true);
7140         
7141         this.fireEvent('changed', this, item, prev);
7142     },
7143     
7144     getBarItem: function(rid)
7145     {
7146         var ret = false;
7147         
7148         Roo.each(this.barItems, function(e) {
7149             if (e.rid != rid) {
7150                 return;
7151             }
7152             
7153             ret =  e;
7154             return false;
7155         });
7156         
7157         return ret;
7158     },
7159     
7160     indexOfItem : function(item)
7161     {
7162         var index = false;
7163         
7164         Roo.each(this.barItems, function(v, i){
7165             
7166             if (v.rid != item.rid) {
7167                 return;
7168             }
7169             
7170             index = i;
7171             return false
7172         });
7173         
7174         return index;
7175     },
7176     
7177     setActiveNext : function()
7178     {
7179         var i = this.indexOfItem(this.getActive());
7180         
7181         if (i > this.barItems.length) {
7182             return;
7183         }
7184         
7185         this.setActiveItem(this.barItems[i+1]);
7186     },
7187     
7188     setActivePrev : function()
7189     {
7190         var i = this.indexOfItem(this.getActive());
7191         
7192         if (i  < 1) {
7193             return;
7194         }
7195         
7196         this.setActiveItem(this.barItems[i-1]);
7197     },
7198     
7199     format : function()
7200     {
7201         if(!this.barItems.length){
7202             return;
7203         }
7204      
7205         var width = 100 / this.barItems.length;
7206         
7207         Roo.each(this.barItems, function(i){
7208             i.el.setStyle('width', width + '%');
7209             i.topEl.el.setStyle('width', width + '%');
7210             i.bottomEl.el.setStyle('width', width + '%');
7211         }, this);
7212         
7213     }
7214     
7215 });
7216 /*
7217  * - LGPL
7218  *
7219  * Nav Progress Item
7220  * 
7221  */
7222
7223 /**
7224  * @class Roo.bootstrap.nav.ProgressBarItem
7225  * @extends Roo.bootstrap.Component
7226  * Bootstrap NavProgressBarItem class
7227  * @cfg {String} rid the reference id
7228  * @cfg {Boolean} active (true|false) Is item active default false
7229  * @cfg {Boolean} disabled (true|false) Is item active default false
7230  * @cfg {String} html
7231  * @cfg {String} position (top|bottom) text position default bottom
7232  * @cfg {String} icon show icon instead of number
7233  * 
7234  * @constructor
7235  * Create a new NavProgressBarItem
7236  * @param {Object} config The config object
7237  */
7238 Roo.bootstrap.nav.ProgressBarItem = function(config){
7239     Roo.bootstrap.nav.ProgressBarItem.superclass.constructor.call(this, config);
7240     this.addEvents({
7241         // raw events
7242         /**
7243          * @event click
7244          * The raw click event for the entire grid.
7245          * @param {Roo.bootstrap.nav.ProgressBarItem} this
7246          * @param {Roo.EventObject} e
7247          */
7248         "click" : true
7249     });
7250    
7251 };
7252
7253 Roo.extend(Roo.bootstrap.nav.ProgressBarItem, Roo.bootstrap.Component,  {
7254     
7255     rid : '',
7256     active : false,
7257     disabled : false,
7258     html : '',
7259     position : 'bottom',
7260     icon : false,
7261     
7262     getAutoCreate : function()
7263     {
7264         var iconCls = 'roo-navigation-bar-item-icon';
7265         
7266         iconCls += ((this.icon) ? (' ' + this.icon) : (' step-number')) ;
7267         
7268         var cfg = {
7269             tag: 'li',
7270             cls: 'roo-navigation-bar-item',
7271             cn : [
7272                 {
7273                     tag : 'i',
7274                     cls : iconCls
7275                 }
7276             ]
7277         };
7278         
7279         if(this.active){
7280             cfg.cls += ' active';
7281         }
7282         if(this.disabled){
7283             cfg.cls += ' disabled';
7284         }
7285         
7286         return cfg;
7287     },
7288     
7289     disable : function()
7290     {
7291         this.setDisabled(true);
7292     },
7293     
7294     enable : function()
7295     {
7296         this.setDisabled(false);
7297     },
7298     
7299     initEvents: function() 
7300     {
7301         this.iconEl = this.el.select('.roo-navigation-bar-item-icon', true).first();
7302         
7303         this.iconEl.on('click', this.onClick, this);
7304     },
7305     
7306     onClick : function(e)
7307     {
7308         e.preventDefault();
7309         
7310         if(this.disabled){
7311             return;
7312         }
7313         
7314         if(this.fireEvent('click', this, e) === false){
7315             return;
7316         };
7317         
7318         this.parent().setActiveItem(this);
7319     },
7320     
7321     isActive: function () 
7322     {
7323         return this.active;
7324     },
7325     
7326     setActive : function(state)
7327     {
7328         if(this.active == state){
7329             return;
7330         }
7331         
7332         this.active = state;
7333         
7334         if (state) {
7335             this.el.addClass('active');
7336             return;
7337         }
7338         
7339         this.el.removeClass('active');
7340         
7341         return;
7342     },
7343     
7344     setDisabled : function(state)
7345     {
7346         if(this.disabled == state){
7347             return;
7348         }
7349         
7350         this.disabled = state;
7351         
7352         if (state) {
7353             this.el.addClass('disabled');
7354             return;
7355         }
7356         
7357         this.el.removeClass('disabled');
7358     },
7359     
7360     tooltipEl : function()
7361     {
7362         return this.el.select('.roo-navigation-bar-item-icon', true).first();;
7363     }
7364 });
7365  
7366
7367  /*
7368  * - LGPL
7369  *
7370  *  Breadcrumb Nav
7371  * 
7372  */
7373 Roo.namespace('Roo.bootstrap.breadcrumb');
7374
7375
7376 /**
7377  * @class Roo.bootstrap.breadcrumb.Nav
7378  * @extends Roo.bootstrap.Component
7379  * Bootstrap Breadcrumb Nav Class
7380  *  
7381  * @children Roo.bootstrap.breadcrumb.Item
7382  * 
7383  * @constructor
7384  * Create a new breadcrumb.Nav
7385  * @param {Object} config The config object
7386  */
7387
7388
7389 Roo.bootstrap.breadcrumb.Nav = function(config){
7390     Roo.bootstrap.breadcrumb.Nav.superclass.constructor.call(this, config);
7391     
7392     
7393 };
7394
7395 Roo.extend(Roo.bootstrap.breadcrumb.Nav, Roo.bootstrap.Component,  {
7396     
7397     getAutoCreate : function()
7398     {
7399
7400         var cfg = {
7401             tag: 'nav',
7402             cn : [
7403                 {
7404                     tag : 'ol',
7405                     cls : 'breadcrumb'
7406                 }
7407             ]
7408             
7409         };
7410           
7411         return cfg;
7412     },
7413     
7414     initEvents: function()
7415     {
7416         this.olEl = this.el.select('ol',true).first();    
7417     },
7418     getChildContainer : function()
7419     {
7420         return this.olEl;  
7421     }
7422     
7423 });
7424
7425  /*
7426  * - LGPL
7427  *
7428  *  Breadcrumb Item
7429  * 
7430  */
7431
7432
7433 /**
7434  * @class Roo.bootstrap.breadcrumb.Nav
7435  * @extends Roo.bootstrap.Component
7436  * @children Roo.bootstrap.Component
7437  * @parent Roo.bootstrap.breadcrumb.Nav
7438  * Bootstrap Breadcrumb Nav Class
7439  *  
7440  * 
7441  * @cfg {String} html the content of the link.
7442  * @cfg {String} href where it links to if '#' is used the link will be handled by onClick.
7443  * @cfg {Boolean} active is it active
7444
7445  * 
7446  * @constructor
7447  * Create a new breadcrumb.Nav
7448  * @param {Object} config The config object
7449  */
7450
7451 Roo.bootstrap.breadcrumb.Item = function(config){
7452     Roo.bootstrap.breadcrumb.Item.superclass.constructor.call(this, config);
7453     this.addEvents({
7454         // img events
7455         /**
7456          * @event click
7457          * The img click event for the img.
7458          * @param {Roo.EventObject} e
7459          */
7460         "click" : true
7461     });
7462     
7463 };
7464
7465 Roo.extend(Roo.bootstrap.breadcrumb.Item, Roo.bootstrap.Component,  {
7466     
7467     href: false,
7468     html : '',
7469     
7470     getAutoCreate : function()
7471     {
7472
7473         var cfg = {
7474             tag: 'li',
7475             cls : 'breadcrumb-item' + (this.active ? ' active' : '')
7476         };
7477         if (this.href !== false) {
7478             cfg.cn = [{
7479                 tag : 'a',
7480                 href : this.href,
7481                 html : this.html
7482             }];
7483         } else {
7484             cfg.html = this.html;
7485         }
7486         
7487         return cfg;
7488     },
7489     
7490     initEvents: function()
7491     {
7492         if (this.href) {
7493             this.el.select('a', true).first().on('click',this.onClick, this)
7494         }
7495         
7496     },
7497     onClick : function(e)
7498     {
7499         e.preventDefault();
7500         this.fireEvent('click',this,  e);
7501     }
7502     
7503 });
7504
7505  /*
7506  * - LGPL
7507  *
7508  * row
7509  * 
7510  */
7511
7512 /**
7513  * @class Roo.bootstrap.Row
7514  * @extends Roo.bootstrap.Component
7515  * @children Roo.bootstrap.Component
7516  * Bootstrap Row class (contains columns...)
7517  * 
7518  * @constructor
7519  * Create a new Row
7520  * @param {Object} config The config object
7521  */
7522
7523 Roo.bootstrap.Row = function(config){
7524     Roo.bootstrap.Row.superclass.constructor.call(this, config);
7525 };
7526
7527 Roo.extend(Roo.bootstrap.Row, Roo.bootstrap.Component,  {
7528     
7529     getAutoCreate : function(){
7530        return {
7531             cls: 'row clearfix'
7532        };
7533     }
7534     
7535     
7536 });
7537
7538  
7539
7540  /*
7541  * - LGPL
7542  *
7543  * pagination
7544  * 
7545  */
7546
7547 /**
7548  * @class Roo.bootstrap.Pagination
7549  * @extends Roo.bootstrap.Component
7550  * @children Roo.bootstrap.Pagination
7551  * Bootstrap Pagination class
7552  * 
7553  * @cfg {String} size (xs|sm|md|lg|xl)
7554  * @cfg {Boolean} inverse 
7555  * 
7556  * @constructor
7557  * Create a new Pagination
7558  * @param {Object} config The config object
7559  */
7560
7561 Roo.bootstrap.Pagination = function(config){
7562     Roo.bootstrap.Pagination.superclass.constructor.call(this, config);
7563 };
7564
7565 Roo.extend(Roo.bootstrap.Pagination, Roo.bootstrap.Component,  {
7566     
7567     cls: false,
7568     size: false,
7569     inverse: false,
7570     
7571     getAutoCreate : function(){
7572         var cfg = {
7573             tag: 'ul',
7574                 cls: 'pagination'
7575         };
7576         if (this.inverse) {
7577             cfg.cls += ' inverse';
7578         }
7579         if (this.html) {
7580             cfg.html=this.html;
7581         }
7582         if (this.cls) {
7583             cfg.cls += " " + this.cls;
7584         }
7585         return cfg;
7586     }
7587    
7588 });
7589
7590  
7591
7592  /*
7593  * - LGPL
7594  *
7595  * Pagination item
7596  * 
7597  */
7598
7599
7600 /**
7601  * @class Roo.bootstrap.PaginationItem
7602  * @extends Roo.bootstrap.Component
7603  * Bootstrap PaginationItem class
7604  * @cfg {String} html text
7605  * @cfg {String} href the link
7606  * @cfg {Boolean} preventDefault (true | false) default true
7607  * @cfg {Boolean} active (true | false) default false
7608  * @cfg {Boolean} disabled default false
7609  * 
7610  * 
7611  * @constructor
7612  * Create a new PaginationItem
7613  * @param {Object} config The config object
7614  */
7615
7616
7617 Roo.bootstrap.PaginationItem = function(config){
7618     Roo.bootstrap.PaginationItem.superclass.constructor.call(this, config);
7619     this.addEvents({
7620         // raw events
7621         /**
7622          * @event click
7623          * The raw click event for the entire grid.
7624          * @param {Roo.EventObject} e
7625          */
7626         "click" : true
7627     });
7628 };
7629
7630 Roo.extend(Roo.bootstrap.PaginationItem, Roo.bootstrap.Component,  {
7631     
7632     href : false,
7633     html : false,
7634     preventDefault: true,
7635     active : false,
7636     cls : false,
7637     disabled: false,
7638     
7639     getAutoCreate : function(){
7640         var cfg= {
7641             tag: 'li',
7642             cn: [
7643                 {
7644                     tag : 'a',
7645                     href : this.href ? this.href : '#',
7646                     html : this.html ? this.html : ''
7647                 }
7648             ]
7649         };
7650         
7651         if(this.cls){
7652             cfg.cls = this.cls;
7653         }
7654         
7655         if(this.disabled){
7656             cfg.cls = typeof(cfg.cls) !== 'undefined' ? cfg.cls + ' disabled' : 'disabled';
7657         }
7658         
7659         if(this.active){
7660             cfg.cls = typeof(cfg.cls) !== 'undefined' ? cfg.cls + ' active' : 'active';
7661         }
7662         
7663         return cfg;
7664     },
7665     
7666     initEvents: function() {
7667         
7668         this.el.on('click', this.onClick, this);
7669         
7670     },
7671     onClick : function(e)
7672     {
7673         Roo.log('PaginationItem on click ');
7674         if(this.preventDefault){
7675             e.preventDefault();
7676         }
7677         
7678         if(this.disabled){
7679             return;
7680         }
7681         
7682         this.fireEvent('click', this, e);
7683     }
7684    
7685 });
7686
7687  
7688
7689  /*
7690  * - LGPL
7691  *
7692  * slider
7693  * 
7694  */
7695
7696
7697 /**
7698  * @class Roo.bootstrap.Slider
7699  * @extends Roo.bootstrap.Component
7700  * Bootstrap Slider class
7701  *    
7702  * @constructor
7703  * Create a new Slider
7704  * @param {Object} config The config object
7705  */
7706
7707 Roo.bootstrap.Slider = function(config){
7708     Roo.bootstrap.Slider.superclass.constructor.call(this, config);
7709 };
7710
7711 Roo.extend(Roo.bootstrap.Slider, Roo.bootstrap.Component,  {
7712     
7713     getAutoCreate : function(){
7714         
7715         var cfg = {
7716             tag: 'div',
7717             cls: 'slider slider-sample1 vertical-handler ui-slider ui-slider-horizontal ui-widget ui-widget-content ui-corner-all',
7718             cn: [
7719                 {
7720                     tag: 'a',
7721                     cls: 'ui-slider-handle ui-state-default ui-corner-all'
7722                 }
7723             ]
7724         };
7725         
7726         return cfg;
7727     }
7728    
7729 });
7730
7731  /*
7732  * Based on:
7733  * Ext JS Library 1.1.1
7734  * Copyright(c) 2006-2007, Ext JS, LLC.
7735  *
7736  * Originally Released Under LGPL - original licence link has changed is not relivant.
7737  *
7738  * Fork - LGPL
7739  * <script type="text/javascript">
7740  */
7741  /**
7742  * @extends Roo.dd.DDProxy
7743  * @class Roo.grid.SplitDragZone
7744  * Support for Column Header resizing
7745  * @constructor
7746  * @param {Object} config
7747  */
7748 // private
7749 // This is a support class used internally by the Grid components
7750 Roo.grid.SplitDragZone = function(grid, hd, hd2){
7751     this.grid = grid;
7752     this.view = grid.getView();
7753     this.proxy = this.view.resizeProxy;
7754     Roo.grid.SplitDragZone.superclass.constructor.call(
7755         this,
7756         hd, // ID
7757         "gridSplitters" + this.grid.getGridEl().id, // SGROUP
7758         {  // CONFIG
7759             dragElId : Roo.id(this.proxy.dom),
7760             resizeFrame:false
7761         }
7762     );
7763     
7764     this.setHandleElId(Roo.id(hd));
7765     if (hd2 !== false) {
7766         this.setOuterHandleElId(Roo.id(hd2));
7767     }
7768     
7769     this.scroll = false;
7770 };
7771 Roo.extend(Roo.grid.SplitDragZone, Roo.dd.DDProxy, {
7772     fly: Roo.Element.fly,
7773
7774     b4StartDrag : function(x, y){
7775         this.view.headersDisabled = true;
7776         var h = this.view.mainWrap ? this.view.mainWrap.getHeight() : (
7777                     this.view.headEl.getHeight() + this.view.bodyEl.getHeight()
7778         );
7779         this.proxy.setHeight(h);
7780         
7781         // for old system colWidth really stored the actual width?
7782         // in bootstrap we tried using xs/ms/etc.. to do % sizing?
7783         // which in reality did not work.. - it worked only for fixed sizes
7784         // for resizable we need to use actual sizes.
7785         var w = this.cm.getColumnWidth(this.cellIndex);
7786         if (!this.view.mainWrap) {
7787             // bootstrap.
7788             w = this.view.getHeaderIndex(this.cellIndex).getWidth();
7789         }
7790         
7791         
7792         
7793         // this was w-this.grid.minColumnWidth;
7794         // doesnt really make sense? - w = thie curren width or the rendered one?
7795         var minw = Math.max(w-this.grid.minColumnWidth, 0);
7796         this.resetConstraints();
7797         this.setXConstraint(minw, 1000);
7798         this.setYConstraint(0, 0);
7799         this.minX = x - minw;
7800         this.maxX = x + 1000;
7801         this.startPos = x;
7802         if (!this.view.mainWrap) { // this is Bootstrap code..
7803             this.getDragEl().style.display='block';
7804         }
7805         
7806         Roo.dd.DDProxy.prototype.b4StartDrag.call(this, x, y);
7807     },
7808
7809
7810     handleMouseDown : function(e){
7811         ev = Roo.EventObject.setEvent(e);
7812         var t = this.fly(ev.getTarget());
7813         if(t.hasClass("x-grid-split")){
7814             this.cellIndex = this.view.getCellIndex(t.dom);
7815             this.split = t.dom;
7816             this.cm = this.grid.colModel;
7817             if(this.cm.isResizable(this.cellIndex) && !this.cm.isFixed(this.cellIndex)){
7818                 Roo.grid.SplitDragZone.superclass.handleMouseDown.apply(this, arguments);
7819             }
7820         }
7821     },
7822
7823     endDrag : function(e){
7824         this.view.headersDisabled = false;
7825         var endX = Math.max(this.minX, Roo.lib.Event.getPageX(e));
7826         var diff = endX - this.startPos;
7827         // 
7828         var w = this.cm.getColumnWidth(this.cellIndex);
7829         if (!this.view.mainWrap) {
7830             w = 0;
7831         }
7832         this.view.onColumnSplitterMoved(this.cellIndex, w+diff);
7833     },
7834
7835     autoOffset : function(){
7836         this.setDelta(0,0);
7837     }
7838 });/*
7839  * Based on:
7840  * Ext JS Library 1.1.1
7841  * Copyright(c) 2006-2007, Ext JS, LLC.
7842  *
7843  * Originally Released Under LGPL - original licence link has changed is not relivant.
7844  *
7845  * Fork - LGPL
7846  * <script type="text/javascript">
7847  */
7848
7849 /**
7850  * @class Roo.grid.AbstractSelectionModel
7851  * @extends Roo.util.Observable
7852  * @abstract
7853  * Abstract base class for grid SelectionModels.  It provides the interface that should be
7854  * implemented by descendant classes.  This class should not be directly instantiated.
7855  * @constructor
7856  */
7857 Roo.grid.AbstractSelectionModel = function(){
7858     this.locked = false;
7859     Roo.grid.AbstractSelectionModel.superclass.constructor.call(this);
7860 };
7861
7862 Roo.extend(Roo.grid.AbstractSelectionModel, Roo.util.Observable,  {
7863     /** @ignore Called by the grid automatically. Do not call directly. */
7864     init : function(grid){
7865         this.grid = grid;
7866         this.initEvents();
7867     },
7868
7869     /**
7870      * Locks the selections.
7871      */
7872     lock : function(){
7873         this.locked = true;
7874     },
7875
7876     /**
7877      * Unlocks the selections.
7878      */
7879     unlock : function(){
7880         this.locked = false;
7881     },
7882
7883     /**
7884      * Returns true if the selections are locked.
7885      * @return {Boolean}
7886      */
7887     isLocked : function(){
7888         return this.locked;
7889     }
7890 });/*
7891  * Based on:
7892  * Ext JS Library 1.1.1
7893  * Copyright(c) 2006-2007, Ext JS, LLC.
7894  *
7895  * Originally Released Under LGPL - original licence link has changed is not relivant.
7896  *
7897  * Fork - LGPL
7898  * <script type="text/javascript">
7899  */
7900 /**
7901  * @extends Roo.grid.AbstractSelectionModel
7902  * @class Roo.grid.RowSelectionModel
7903  * The default SelectionModel used by {@link Roo.grid.Grid}.
7904  * It supports multiple selections and keyboard selection/navigation. 
7905  * @constructor
7906  * @param {Object} config
7907  */
7908 Roo.grid.RowSelectionModel = function(config){
7909     Roo.apply(this, config);
7910     this.selections = new Roo.util.MixedCollection(false, function(o){
7911         return o.id;
7912     });
7913
7914     this.last = false;
7915     this.lastActive = false;
7916
7917     this.addEvents({
7918         /**
7919         * @event selectionchange
7920         * Fires when the selection changes
7921         * @param {SelectionModel} this
7922         */
7923        "selectionchange" : true,
7924        /**
7925         * @event afterselectionchange
7926         * Fires after the selection changes (eg. by key press or clicking)
7927         * @param {SelectionModel} this
7928         */
7929        "afterselectionchange" : true,
7930        /**
7931         * @event beforerowselect
7932         * Fires when a row is selected being selected, return false to cancel.
7933         * @param {SelectionModel} this
7934         * @param {Number} rowIndex The selected index
7935         * @param {Boolean} keepExisting False if other selections will be cleared
7936         */
7937        "beforerowselect" : true,
7938        /**
7939         * @event rowselect
7940         * Fires when a row is selected.
7941         * @param {SelectionModel} this
7942         * @param {Number} rowIndex The selected index
7943         * @param {Roo.data.Record} r The record
7944         */
7945        "rowselect" : true,
7946        /**
7947         * @event rowdeselect
7948         * Fires when a row is deselected.
7949         * @param {SelectionModel} this
7950         * @param {Number} rowIndex The selected index
7951         */
7952         "rowdeselect" : true
7953     });
7954     Roo.grid.RowSelectionModel.superclass.constructor.call(this);
7955     this.locked = false;
7956 };
7957
7958 Roo.extend(Roo.grid.RowSelectionModel, Roo.grid.AbstractSelectionModel,  {
7959     /**
7960      * @cfg {Boolean} singleSelect
7961      * True to allow selection of only one row at a time (defaults to false)
7962      */
7963     singleSelect : false,
7964
7965     // private
7966     initEvents : function(){
7967
7968         if(!this.grid.enableDragDrop && !this.grid.enableDrag){
7969             this.grid.on("mousedown", this.handleMouseDown, this);
7970         }else{ // allow click to work like normal
7971             this.grid.on("rowclick", this.handleDragableRowClick, this);
7972         }
7973         // bootstrap does not have a view..
7974         var view = this.grid.view ? this.grid.view : this.grid;
7975         this.rowNav = new Roo.KeyNav(this.grid.getGridEl(), {
7976             "up" : function(e){
7977                 if(!e.shiftKey){
7978                     this.selectPrevious(e.shiftKey);
7979                 }else if(this.last !== false && this.lastActive !== false){
7980                     var last = this.last;
7981                     this.selectRange(this.last,  this.lastActive-1);
7982                     view.focusRow(this.lastActive);
7983                     if(last !== false){
7984                         this.last = last;
7985                     }
7986                 }else{
7987                     this.selectFirstRow();
7988                 }
7989                 this.fireEvent("afterselectionchange", this);
7990             },
7991             "down" : function(e){
7992                 if(!e.shiftKey){
7993                     this.selectNext(e.shiftKey);
7994                 }else if(this.last !== false && this.lastActive !== false){
7995                     var last = this.last;
7996                     this.selectRange(this.last,  this.lastActive+1);
7997                     view.focusRow(this.lastActive);
7998                     if(last !== false){
7999                         this.last = last;
8000                     }
8001                 }else{
8002                     this.selectFirstRow();
8003                 }
8004                 this.fireEvent("afterselectionchange", this);
8005             },
8006             scope: this
8007         });
8008
8009          
8010         view.on("refresh", this.onRefresh, this);
8011         view.on("rowupdated", this.onRowUpdated, this);
8012         view.on("rowremoved", this.onRemove, this);
8013     },
8014
8015     // private
8016     onRefresh : function(){
8017         var ds = this.grid.ds, i, v = this.grid.view;
8018         var s = this.selections;
8019         s.each(function(r){
8020             if((i = ds.indexOfId(r.id)) != -1){
8021                 v.onRowSelect(i);
8022                 s.add(ds.getAt(i)); // updating the selection relate data
8023             }else{
8024                 s.remove(r);
8025             }
8026         });
8027     },
8028
8029     // private
8030     onRemove : function(v, index, r){
8031         this.selections.remove(r);
8032     },
8033
8034     // private
8035     onRowUpdated : function(v, index, r){
8036         if(this.isSelected(r)){
8037             v.onRowSelect(index);
8038         }
8039     },
8040
8041     /**
8042      * Select records.
8043      * @param {Array} records The records to select
8044      * @param {Boolean} keepExisting (optional) True to keep existing selections
8045      */
8046     selectRecords : function(records, keepExisting){
8047         if(!keepExisting){
8048             this.clearSelections();
8049         }
8050         var ds = this.grid.ds;
8051         for(var i = 0, len = records.length; i < len; i++){
8052             this.selectRow(ds.indexOf(records[i]), true);
8053         }
8054     },
8055
8056     /**
8057      * Gets the number of selected rows.
8058      * @return {Number}
8059      */
8060     getCount : function(){
8061         return this.selections.length;
8062     },
8063
8064     /**
8065      * Selects the first row in the grid.
8066      */
8067     selectFirstRow : function(){
8068         this.selectRow(0);
8069     },
8070
8071     /**
8072      * Select the last row.
8073      * @param {Boolean} keepExisting (optional) True to keep existing selections
8074      */
8075     selectLastRow : function(keepExisting){
8076         this.selectRow(this.grid.ds.getCount() - 1, keepExisting);
8077     },
8078
8079     /**
8080      * Selects the row immediately following the last selected row.
8081      * @param {Boolean} keepExisting (optional) True to keep existing selections
8082      */
8083     selectNext : function(keepExisting){
8084         if(this.last !== false && (this.last+1) < this.grid.ds.getCount()){
8085             this.selectRow(this.last+1, keepExisting);
8086             var view = this.grid.view ? this.grid.view : this.grid;
8087             view.focusRow(this.last);
8088         }
8089     },
8090
8091     /**
8092      * Selects the row that precedes the last selected row.
8093      * @param {Boolean} keepExisting (optional) True to keep existing selections
8094      */
8095     selectPrevious : function(keepExisting){
8096         if(this.last){
8097             this.selectRow(this.last-1, keepExisting);
8098             var view = this.grid.view ? this.grid.view : this.grid;
8099             view.focusRow(this.last);
8100         }
8101     },
8102
8103     /**
8104      * Returns the selected records
8105      * @return {Array} Array of selected records
8106      */
8107     getSelections : function(){
8108         return [].concat(this.selections.items);
8109     },
8110
8111     /**
8112      * Returns the first selected record.
8113      * @return {Record}
8114      */
8115     getSelected : function(){
8116         return this.selections.itemAt(0);
8117     },
8118
8119
8120     /**
8121      * Clears all selections.
8122      */
8123     clearSelections : function(fast){
8124         if(this.locked) {
8125             return;
8126         }
8127         if(fast !== true){
8128             var ds = this.grid.ds;
8129             var s = this.selections;
8130             s.each(function(r){
8131                 this.deselectRow(ds.indexOfId(r.id));
8132             }, this);
8133             s.clear();
8134         }else{
8135             this.selections.clear();
8136         }
8137         this.last = false;
8138     },
8139
8140
8141     /**
8142      * Selects all rows.
8143      */
8144     selectAll : function(){
8145         if(this.locked) {
8146             return;
8147         }
8148         this.selections.clear();
8149         for(var i = 0, len = this.grid.ds.getCount(); i < len; i++){
8150             this.selectRow(i, true);
8151         }
8152     },
8153
8154     /**
8155      * Returns True if there is a selection.
8156      * @return {Boolean}
8157      */
8158     hasSelection : function(){
8159         return this.selections.length > 0;
8160     },
8161
8162     /**
8163      * Returns True if the specified row is selected.
8164      * @param {Number/Record} record The record or index of the record to check
8165      * @return {Boolean}
8166      */
8167     isSelected : function(index){
8168         var r = typeof index == "number" ? this.grid.ds.getAt(index) : index;
8169         return (r && this.selections.key(r.id) ? true : false);
8170     },
8171
8172     /**
8173      * Returns True if the specified record id is selected.
8174      * @param {String} id The id of record to check
8175      * @return {Boolean}
8176      */
8177     isIdSelected : function(id){
8178         return (this.selections.key(id) ? true : false);
8179     },
8180
8181     // private
8182     handleMouseDown : function(e, t)
8183     {
8184         var view = this.grid.view ? this.grid.view : this.grid;
8185         var rowIndex;
8186         if(this.isLocked() || (rowIndex = view.findRowIndex(t)) === false){
8187             return;
8188         };
8189         if(e.shiftKey && this.last !== false){
8190             var last = this.last;
8191             this.selectRange(last, rowIndex, e.ctrlKey);
8192             this.last = last; // reset the last
8193             view.focusRow(rowIndex);
8194         }else{
8195             var isSelected = this.isSelected(rowIndex);
8196             if(e.button !== 0 && isSelected){
8197                 view.focusRow(rowIndex);
8198             }else if(e.ctrlKey && isSelected){
8199                 this.deselectRow(rowIndex);
8200             }else if(!isSelected){
8201                 this.selectRow(rowIndex, e.button === 0 && (e.ctrlKey || e.shiftKey));
8202                 view.focusRow(rowIndex);
8203             }
8204         }
8205         this.fireEvent("afterselectionchange", this);
8206     },
8207     // private
8208     handleDragableRowClick :  function(grid, rowIndex, e) 
8209     {
8210         if(e.button === 0 && !e.shiftKey && !e.ctrlKey) {
8211             this.selectRow(rowIndex, false);
8212             var view = this.grid.view ? this.grid.view : this.grid;
8213             view.focusRow(rowIndex);
8214              this.fireEvent("afterselectionchange", this);
8215         }
8216     },
8217     
8218     /**
8219      * Selects multiple rows.
8220      * @param {Array} rows Array of the indexes of the row to select
8221      * @param {Boolean} keepExisting (optional) True to keep existing selections
8222      */
8223     selectRows : function(rows, keepExisting){
8224         if(!keepExisting){
8225             this.clearSelections();
8226         }
8227         for(var i = 0, len = rows.length; i < len; i++){
8228             this.selectRow(rows[i], true);
8229         }
8230     },
8231
8232     /**
8233      * Selects a range of rows. All rows in between startRow and endRow are also selected.
8234      * @param {Number} startRow The index of the first row in the range
8235      * @param {Number} endRow The index of the last row in the range
8236      * @param {Boolean} keepExisting (optional) True to retain existing selections
8237      */
8238     selectRange : function(startRow, endRow, keepExisting){
8239         if(this.locked) {
8240             return;
8241         }
8242         if(!keepExisting){
8243             this.clearSelections();
8244         }
8245         if(startRow <= endRow){
8246             for(var i = startRow; i <= endRow; i++){
8247                 this.selectRow(i, true);
8248             }
8249         }else{
8250             for(var i = startRow; i >= endRow; i--){
8251                 this.selectRow(i, true);
8252             }
8253         }
8254     },
8255
8256     /**
8257      * Deselects a range of rows. All rows in between startRow and endRow are also deselected.
8258      * @param {Number} startRow The index of the first row in the range
8259      * @param {Number} endRow The index of the last row in the range
8260      */
8261     deselectRange : function(startRow, endRow, preventViewNotify){
8262         if(this.locked) {
8263             return;
8264         }
8265         for(var i = startRow; i <= endRow; i++){
8266             this.deselectRow(i, preventViewNotify);
8267         }
8268     },
8269
8270     /**
8271      * Selects a row.
8272      * @param {Number} row The index of the row to select
8273      * @param {Boolean} keepExisting (optional) True to keep existing selections
8274      */
8275     selectRow : function(index, keepExisting, preventViewNotify){
8276         if(this.locked || (index < 0 || index >= this.grid.ds.getCount())) {
8277             return;
8278         }
8279         if(this.fireEvent("beforerowselect", this, index, keepExisting) !== false){
8280             if(!keepExisting || this.singleSelect){
8281                 this.clearSelections();
8282             }
8283             var r = this.grid.ds.getAt(index);
8284             this.selections.add(r);
8285             this.last = this.lastActive = index;
8286             if(!preventViewNotify){
8287                 var view = this.grid.view ? this.grid.view : this.grid;
8288                 view.onRowSelect(index);
8289             }
8290             this.fireEvent("rowselect", this, index, r);
8291             this.fireEvent("selectionchange", this);
8292         }
8293     },
8294
8295     /**
8296      * Deselects a row.
8297      * @param {Number} row The index of the row to deselect
8298      */
8299     deselectRow : function(index, preventViewNotify){
8300         if(this.locked) {
8301             return;
8302         }
8303         if(this.last == index){
8304             this.last = false;
8305         }
8306         if(this.lastActive == index){
8307             this.lastActive = false;
8308         }
8309         var r = this.grid.ds.getAt(index);
8310         this.selections.remove(r);
8311         if(!preventViewNotify){
8312             var view = this.grid.view ? this.grid.view : this.grid;
8313             view.onRowDeselect(index);
8314         }
8315         this.fireEvent("rowdeselect", this, index);
8316         this.fireEvent("selectionchange", this);
8317     },
8318
8319     // private
8320     restoreLast : function(){
8321         if(this._last){
8322             this.last = this._last;
8323         }
8324     },
8325
8326     // private
8327     acceptsNav : function(row, col, cm){
8328         return !cm.isHidden(col) && cm.isCellEditable(col, row);
8329     },
8330
8331     // private
8332     onEditorKey : function(field, e){
8333         var k = e.getKey(), newCell, g = this.grid, ed = g.activeEditor;
8334         if(k == e.TAB){
8335             e.stopEvent();
8336             ed.completeEdit();
8337             if(e.shiftKey){
8338                 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
8339             }else{
8340                 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
8341             }
8342         }else if(k == e.ENTER && !e.ctrlKey){
8343             e.stopEvent();
8344             ed.completeEdit();
8345             if(e.shiftKey){
8346                 newCell = g.walkCells(ed.row-1, ed.col, -1, this.acceptsNav, this);
8347             }else{
8348                 newCell = g.walkCells(ed.row+1, ed.col, 1, this.acceptsNav, this);
8349             }
8350         }else if(k == e.ESC){
8351             ed.cancelEdit();
8352         }
8353         if(newCell){
8354             g.startEditing(newCell[0], newCell[1]);
8355         }
8356     }
8357 });/*
8358  * Based on:
8359  * Ext JS Library 1.1.1
8360  * Copyright(c) 2006-2007, Ext JS, LLC.
8361  *
8362  * Originally Released Under LGPL - original licence link has changed is not relivant.
8363  *
8364  * Fork - LGPL
8365  * <script type="text/javascript">
8366  */
8367  
8368
8369 /**
8370  * @class Roo.grid.ColumnModel
8371  * @extends Roo.util.Observable
8372  * This is the default implementation of a ColumnModel used by the Grid. It defines
8373  * the columns in the grid.
8374  * <br>Usage:<br>
8375  <pre><code>
8376  var colModel = new Roo.grid.ColumnModel([
8377         {header: "Ticker", width: 60, sortable: true, locked: true},
8378         {header: "Company Name", width: 150, sortable: true},
8379         {header: "Market Cap.", width: 100, sortable: true},
8380         {header: "$ Sales", width: 100, sortable: true, renderer: money},
8381         {header: "Employees", width: 100, sortable: true, resizable: false}
8382  ]);
8383  </code></pre>
8384  * <p>
8385  
8386  * The config options listed for this class are options which may appear in each
8387  * individual column definition.
8388  * <br/>RooJS Fix - column id's are not sequential but use Roo.id() - fixes bugs with layouts.
8389  * @constructor
8390  * @param {Object} config An Array of column config objects. See this class's
8391  * config objects for details.
8392 */
8393 Roo.grid.ColumnModel = function(config){
8394         /**
8395      * The config passed into the constructor
8396      */
8397     this.config = []; //config;
8398     this.lookup = {};
8399
8400     // if no id, create one
8401     // if the column does not have a dataIndex mapping,
8402     // map it to the order it is in the config
8403     for(var i = 0, len = config.length; i < len; i++){
8404         this.addColumn(config[i]);
8405         
8406     }
8407
8408     /**
8409      * The width of columns which have no width specified (defaults to 100)
8410      * @type Number
8411      */
8412     this.defaultWidth = 100;
8413
8414     /**
8415      * Default sortable of columns which have no sortable specified (defaults to false)
8416      * @type Boolean
8417      */
8418     this.defaultSortable = false;
8419
8420     this.addEvents({
8421         /**
8422              * @event widthchange
8423              * Fires when the width of a column changes.
8424              * @param {ColumnModel} this
8425              * @param {Number} columnIndex The column index
8426              * @param {Number} newWidth The new width
8427              */
8428             "widthchange": true,
8429         /**
8430              * @event headerchange
8431              * Fires when the text of a header changes.
8432              * @param {ColumnModel} this
8433              * @param {Number} columnIndex The column index
8434              * @param {Number} newText The new header text
8435              */
8436             "headerchange": true,
8437         /**
8438              * @event hiddenchange
8439              * Fires when a column is hidden or "unhidden".
8440              * @param {ColumnModel} this
8441              * @param {Number} columnIndex The column index
8442              * @param {Boolean} hidden true if hidden, false otherwise
8443              */
8444             "hiddenchange": true,
8445             /**
8446          * @event columnmoved
8447          * Fires when a column is moved.
8448          * @param {ColumnModel} this
8449          * @param {Number} oldIndex
8450          * @param {Number} newIndex
8451          */
8452         "columnmoved" : true,
8453         /**
8454          * @event columlockchange
8455          * Fires when a column's locked state is changed
8456          * @param {ColumnModel} this
8457          * @param {Number} colIndex
8458          * @param {Boolean} locked true if locked
8459          */
8460         "columnlockchange" : true
8461     });
8462     Roo.grid.ColumnModel.superclass.constructor.call(this);
8463 };
8464 Roo.extend(Roo.grid.ColumnModel, Roo.util.Observable, {
8465     /**
8466      * @cfg {String} header [required] The header text to display in the Grid view.
8467      */
8468         /**
8469      * @cfg {String} xsHeader Header at Bootsrap Extra Small width (default for all)
8470      */
8471         /**
8472      * @cfg {String} smHeader Header at Bootsrap Small width
8473      */
8474         /**
8475      * @cfg {String} mdHeader Header at Bootsrap Medium width
8476      */
8477         /**
8478      * @cfg {String} lgHeader Header at Bootsrap Large width
8479      */
8480         /**
8481      * @cfg {String} xlHeader Header at Bootsrap extra Large width
8482      */
8483     /**
8484      * @cfg {String} dataIndex  The name of the field in the grid's {@link Roo.data.Store}'s
8485      * {@link Roo.data.Record} definition from which to draw the column's value. If not
8486      * specified, the column's index is used as an index into the Record's data Array.
8487      */
8488     /**
8489      * @cfg {Number} width  The initial width in pixels of the column. Using this
8490      * instead of {@link Roo.grid.Grid#autoSizeColumns} is more efficient.
8491      */
8492     /**
8493      * @cfg {Boolean} sortable True if sorting is to be allowed on this column.
8494      * Defaults to the value of the {@link #defaultSortable} property.
8495      * Whether local/remote sorting is used is specified in {@link Roo.data.Store#remoteSort}.
8496      */
8497     /**
8498      * @cfg {Boolean} locked  True to lock the column in place while scrolling the Grid.  Defaults to false.
8499      */
8500     /**
8501      * @cfg {Boolean} fixed  True if the column width cannot be changed.  Defaults to false.
8502      */
8503     /**
8504      * @cfg {Boolean} resizable  False to disable column resizing. Defaults to true.
8505      */
8506     /**
8507      * @cfg {Boolean} hidden  True to hide the column. Defaults to false.
8508      */
8509     /**
8510      * @cfg {Function} renderer A function used to generate HTML markup for a cell
8511      * given the cell's data value. See {@link #setRenderer}. If not specified, the
8512      * default renderer returns the escaped data value. If an object is returned (bootstrap only)
8513      * then it is treated as a Roo Component object instance, and it is rendered after the initial row is rendered
8514      */
8515        /**
8516      * @cfg {Roo.grid.GridEditor} editor  For grid editors - returns the grid editor 
8517      */
8518     /**
8519      * @cfg {String} align (left|right) Set the CSS text-align property of the column.  Defaults to undefined (left).
8520      */
8521     /**
8522      * @cfg {String} valign (top|bottom|middle) Set the CSS vertical-align property of the column (eg. middle, top, bottom etc).  Defaults to undefined (middle)
8523      */
8524     /**
8525      * @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)
8526      */
8527     /**
8528      * @cfg {String} tooltip mouse over tooltip text
8529      */
8530     /**
8531      * @cfg {Number} xs  can be '0' for hidden at this size (number less than 12)
8532      */
8533     /**
8534      * @cfg {Number} sm can be '0' for hidden at this size (number less than 12)
8535      */
8536     /**
8537      * @cfg {Number} md can be '0' for hidden at this size (number less than 12)
8538      */
8539     /**
8540      * @cfg {Number} lg   can be '0' for hidden at this size (number less than 12)
8541      */
8542         /**
8543      * @cfg {Number} xl   can be '0' for hidden at this size (number less than 12)
8544      */
8545     /**
8546      * Returns the id of the column at the specified index.
8547      * @param {Number} index The column index
8548      * @return {String} the id
8549      */
8550     getColumnId : function(index){
8551         return this.config[index].id;
8552     },
8553
8554     /**
8555      * Returns the column for a specified id.
8556      * @param {String} id The column id
8557      * @return {Object} the column
8558      */
8559     getColumnById : function(id){
8560         return this.lookup[id];
8561     },
8562
8563     
8564     /**
8565      * Returns the column Object for a specified dataIndex.
8566      * @param {String} dataIndex The column dataIndex
8567      * @return {Object|Boolean} the column or false if not found
8568      */
8569     getColumnByDataIndex: function(dataIndex){
8570         var index = this.findColumnIndex(dataIndex);
8571         return index > -1 ? this.config[index] : false;
8572     },
8573     
8574     /**
8575      * Returns the index for a specified column id.
8576      * @param {String} id The column id
8577      * @return {Number} the index, or -1 if not found
8578      */
8579     getIndexById : function(id){
8580         for(var i = 0, len = this.config.length; i < len; i++){
8581             if(this.config[i].id == id){
8582                 return i;
8583             }
8584         }
8585         return -1;
8586     },
8587     
8588     /**
8589      * Returns the index for a specified column dataIndex.
8590      * @param {String} dataIndex The column dataIndex
8591      * @return {Number} the index, or -1 if not found
8592      */
8593     
8594     findColumnIndex : function(dataIndex){
8595         for(var i = 0, len = this.config.length; i < len; i++){
8596             if(this.config[i].dataIndex == dataIndex){
8597                 return i;
8598             }
8599         }
8600         return -1;
8601     },
8602     
8603     
8604     moveColumn : function(oldIndex, newIndex){
8605         var c = this.config[oldIndex];
8606         this.config.splice(oldIndex, 1);
8607         this.config.splice(newIndex, 0, c);
8608         this.dataMap = null;
8609         this.fireEvent("columnmoved", this, oldIndex, newIndex);
8610     },
8611
8612     isLocked : function(colIndex){
8613         return this.config[colIndex].locked === true;
8614     },
8615
8616     setLocked : function(colIndex, value, suppressEvent){
8617         if(this.isLocked(colIndex) == value){
8618             return;
8619         }
8620         this.config[colIndex].locked = value;
8621         if(!suppressEvent){
8622             this.fireEvent("columnlockchange", this, colIndex, value);
8623         }
8624     },
8625
8626     getTotalLockedWidth : function(){
8627         var totalWidth = 0;
8628         for(var i = 0; i < this.config.length; i++){
8629             if(this.isLocked(i) && !this.isHidden(i)){
8630                 this.totalWidth += this.getColumnWidth(i);
8631             }
8632         }
8633         return totalWidth;
8634     },
8635
8636     getLockedCount : function(){
8637         for(var i = 0, len = this.config.length; i < len; i++){
8638             if(!this.isLocked(i)){
8639                 return i;
8640             }
8641         }
8642         
8643         return this.config.length;
8644     },
8645
8646     /**
8647      * Returns the number of columns.
8648      * @return {Number}
8649      */
8650     getColumnCount : function(visibleOnly){
8651         if(visibleOnly === true){
8652             var c = 0;
8653             for(var i = 0, len = this.config.length; i < len; i++){
8654                 if(!this.isHidden(i)){
8655                     c++;
8656                 }
8657             }
8658             return c;
8659         }
8660         return this.config.length;
8661     },
8662
8663     /**
8664      * Returns the column configs that return true by the passed function that is called with (columnConfig, index)
8665      * @param {Function} fn
8666      * @param {Object} scope (optional)
8667      * @return {Array} result
8668      */
8669     getColumnsBy : function(fn, scope){
8670         var r = [];
8671         for(var i = 0, len = this.config.length; i < len; i++){
8672             var c = this.config[i];
8673             if(fn.call(scope||this, c, i) === true){
8674                 r[r.length] = c;
8675             }
8676         }
8677         return r;
8678     },
8679
8680     /**
8681      * Returns true if the specified column is sortable.
8682      * @param {Number} col The column index
8683      * @return {Boolean}
8684      */
8685     isSortable : function(col){
8686         if(typeof this.config[col].sortable == "undefined"){
8687             return this.defaultSortable;
8688         }
8689         return this.config[col].sortable;
8690     },
8691
8692     /**
8693      * Returns the rendering (formatting) function defined for the column.
8694      * @param {Number} col The column index.
8695      * @return {Function} The function used to render the cell. See {@link #setRenderer}.
8696      */
8697     getRenderer : function(col){
8698         if(!this.config[col].renderer){
8699             return Roo.grid.ColumnModel.defaultRenderer;
8700         }
8701         return this.config[col].renderer;
8702     },
8703
8704     /**
8705      * Sets the rendering (formatting) function for a column.
8706      * @param {Number} col The column index
8707      * @param {Function} fn The function to use to process the cell's raw data
8708      * to return HTML markup for the grid view. The render function is called with
8709      * the following parameters:<ul>
8710      * <li>Data value.</li>
8711      * <li>Cell metadata. An object in which you may set the following attributes:<ul>
8712      * <li>css A CSS style string to apply to the table cell.</li>
8713      * <li>attr An HTML attribute definition string to apply to the data container element <i>within</i> the table cell.</li></ul>
8714      * <li>The {@link Roo.data.Record} from which the data was extracted.</li>
8715      * <li>Row index</li>
8716      * <li>Column index</li>
8717      * <li>The {@link Roo.data.Store} object from which the Record was extracted</li></ul>
8718      */
8719     setRenderer : function(col, fn){
8720         this.config[col].renderer = fn;
8721     },
8722
8723     /**
8724      * Returns the width for the specified column.
8725      * @param {Number} col The column index
8726      * @param (optional) {String} gridSize bootstrap width size.
8727      * @return {Number}
8728      */
8729     getColumnWidth : function(col, gridSize)
8730         {
8731                 var cfg = this.config[col];
8732                 
8733                 if (typeof(gridSize) == 'undefined') {
8734                         return cfg.width * 1 || this.defaultWidth;
8735                 }
8736                 if (gridSize === false) { // if we set it..
8737                         return cfg.width || false;
8738                 }
8739                 var sizes = ['xl', 'lg', 'md', 'sm', 'xs'];
8740                 
8741                 for(var i = sizes.indexOf(gridSize); i < sizes.length; i++) {
8742                         if (typeof(cfg[ sizes[i] ] ) == 'undefined') {
8743                                 continue;
8744                         }
8745                         return cfg[ sizes[i] ];
8746                 }
8747                 return 1;
8748                 
8749     },
8750
8751     /**
8752      * Sets the width for a column.
8753      * @param {Number} col The column index
8754      * @param {Number} width The new width
8755      */
8756     setColumnWidth : function(col, width, suppressEvent){
8757         this.config[col].width = width;
8758         this.totalWidth = null;
8759         if(!suppressEvent){
8760              this.fireEvent("widthchange", this, col, width);
8761         }
8762     },
8763
8764     /**
8765      * Returns the total width of all columns.
8766      * @param {Boolean} includeHidden True to include hidden column widths
8767      * @return {Number}
8768      */
8769     getTotalWidth : function(includeHidden){
8770         if(!this.totalWidth){
8771             this.totalWidth = 0;
8772             for(var i = 0, len = this.config.length; i < len; i++){
8773                 if(includeHidden || !this.isHidden(i)){
8774                     this.totalWidth += this.getColumnWidth(i);
8775                 }
8776             }
8777         }
8778         return this.totalWidth;
8779     },
8780
8781     /**
8782      * Returns the header for the specified column.
8783      * @param {Number} col The column index
8784      * @return {String}
8785      */
8786     getColumnHeader : function(col){
8787         return this.config[col].header;
8788     },
8789
8790     /**
8791      * Sets the header for a column.
8792      * @param {Number} col The column index
8793      * @param {String} header The new header
8794      */
8795     setColumnHeader : function(col, header){
8796         this.config[col].header = header;
8797         this.fireEvent("headerchange", this, col, header);
8798     },
8799
8800     /**
8801      * Returns the tooltip for the specified column.
8802      * @param {Number} col The column index
8803      * @return {String}
8804      */
8805     getColumnTooltip : function(col){
8806             return this.config[col].tooltip;
8807     },
8808     /**
8809      * Sets the tooltip for a column.
8810      * @param {Number} col The column index
8811      * @param {String} tooltip The new tooltip
8812      */
8813     setColumnTooltip : function(col, tooltip){
8814             this.config[col].tooltip = tooltip;
8815     },
8816
8817     /**
8818      * Returns the dataIndex for the specified column.
8819      * @param {Number} col The column index
8820      * @return {Number}
8821      */
8822     getDataIndex : function(col){
8823         return this.config[col].dataIndex;
8824     },
8825
8826     /**
8827      * Sets the dataIndex for a column.
8828      * @param {Number} col The column index
8829      * @param {Number} dataIndex The new dataIndex
8830      */
8831     setDataIndex : function(col, dataIndex){
8832         this.config[col].dataIndex = dataIndex;
8833     },
8834
8835     
8836     
8837     /**
8838      * Returns true if the cell is editable.
8839      * @param {Number} colIndex The column index
8840      * @param {Number} rowIndex The row index - this is nto actually used..?
8841      * @return {Boolean}
8842      */
8843     isCellEditable : function(colIndex, rowIndex){
8844         return (this.config[colIndex].editable || (typeof this.config[colIndex].editable == "undefined" && this.config[colIndex].editor)) ? true : false;
8845     },
8846
8847     /**
8848      * Returns the editor defined for the cell/column.
8849      * return false or null to disable editing.
8850      * @param {Number} colIndex The column index
8851      * @param {Number} rowIndex The row index
8852      * @return {Object}
8853      */
8854     getCellEditor : function(colIndex, rowIndex){
8855         return this.config[colIndex].editor;
8856     },
8857
8858     /**
8859      * Sets if a column is editable.
8860      * @param {Number} col The column index
8861      * @param {Boolean} editable True if the column is editable
8862      */
8863     setEditable : function(col, editable){
8864         this.config[col].editable = editable;
8865     },
8866
8867
8868     /**
8869      * Returns true if the column is hidden.
8870      * @param {Number} colIndex The column index
8871      * @return {Boolean}
8872      */
8873     isHidden : function(colIndex){
8874         return this.config[colIndex].hidden;
8875     },
8876
8877
8878     /**
8879      * Returns true if the column width cannot be changed
8880      */
8881     isFixed : function(colIndex){
8882         return this.config[colIndex].fixed;
8883     },
8884
8885     /**
8886      * Returns true if the column can be resized
8887      * @return {Boolean}
8888      */
8889     isResizable : function(colIndex){
8890         return colIndex >= 0 && this.config[colIndex].resizable !== false && this.config[colIndex].fixed !== true;
8891     },
8892     /**
8893      * Sets if a column is hidden.
8894      * @param {Number} colIndex The column index
8895      * @param {Boolean} hidden True if the column is hidden
8896      */
8897     setHidden : function(colIndex, hidden){
8898         this.config[colIndex].hidden = hidden;
8899         this.totalWidth = null;
8900         this.fireEvent("hiddenchange", this, colIndex, hidden);
8901     },
8902
8903     /**
8904      * Sets the editor for a column.
8905      * @param {Number} col The column index
8906      * @param {Object} editor The editor object
8907      */
8908     setEditor : function(col, editor){
8909         this.config[col].editor = editor;
8910     },
8911     /**
8912      * Add a column (experimental...) - defaults to adding to the end..
8913      * @param {Object} config 
8914     */
8915     addColumn : function(c)
8916     {
8917     
8918         var i = this.config.length;
8919         this.config[i] = c;
8920         
8921         if(typeof c.dataIndex == "undefined"){
8922             c.dataIndex = i;
8923         }
8924         if(typeof c.renderer == "string"){
8925             c.renderer = Roo.util.Format[c.renderer];
8926         }
8927         if(typeof c.id == "undefined"){
8928             c.id = Roo.id();
8929         }
8930         if(c.editor && c.editor.xtype){
8931             c.editor  = Roo.factory(c.editor, Roo.grid);
8932         }
8933         if(c.editor && c.editor.isFormField){
8934             c.editor = new Roo.grid.GridEditor(c.editor);
8935         }
8936         this.lookup[c.id] = c;
8937     }
8938     
8939 });
8940
8941 Roo.grid.ColumnModel.defaultRenderer = function(value)
8942 {
8943     if(typeof value == "object") {
8944         return value;
8945     }
8946         if(typeof value == "string" && value.length < 1){
8947             return "&#160;";
8948         }
8949     
8950         return String.format("{0}", value);
8951 };
8952
8953 // Alias for backwards compatibility
8954 Roo.grid.DefaultColumnModel = Roo.grid.ColumnModel;
8955 /*
8956  * Based on:
8957  * Ext JS Library 1.1.1
8958  * Copyright(c) 2006-2007, Ext JS, LLC.
8959  *
8960  * Originally Released Under LGPL - original licence link has changed is not relivant.
8961  *
8962  * Fork - LGPL
8963  * <script type="text/javascript">
8964  */
8965  
8966 /**
8967  * @class Roo.LoadMask
8968  * A simple utility class for generically masking elements while loading data.  If the element being masked has
8969  * an underlying {@link Roo.data.Store}, the masking will be automatically synchronized with the store's loading
8970  * process and the mask element will be cached for reuse.  For all other elements, this mask will replace the
8971  * element's UpdateManager load indicator and will be destroyed after the initial load.
8972  * @constructor
8973  * Create a new LoadMask
8974  * @param {String/HTMLElement/Roo.Element} el The element or DOM node, or its id
8975  * @param {Object} config The config object
8976  */
8977 Roo.LoadMask = function(el, config){
8978     this.el = Roo.get(el);
8979     Roo.apply(this, config);
8980     if(this.store){
8981         this.store.on('beforeload', this.onBeforeLoad, this);
8982         this.store.on('load', this.onLoad, this);
8983         this.store.on('loadexception', this.onLoadException, this);
8984         this.removeMask = false;
8985     }else{
8986         var um = this.el.getUpdateManager();
8987         um.showLoadIndicator = false; // disable the default indicator
8988         um.on('beforeupdate', this.onBeforeLoad, this);
8989         um.on('update', this.onLoad, this);
8990         um.on('failure', this.onLoad, this);
8991         this.removeMask = true;
8992     }
8993 };
8994
8995 Roo.LoadMask.prototype = {
8996     /**
8997      * @cfg {Boolean} removeMask
8998      * True to create a single-use mask that is automatically destroyed after loading (useful for page loads),
8999      * False to persist the mask element reference for multiple uses (e.g., for paged data widgets).  Defaults to false.
9000      */
9001     removeMask : false,
9002     /**
9003      * @cfg {String} msg
9004      * The text to display in a centered loading message box (defaults to 'Loading...')
9005      */
9006     msg : 'Loading...',
9007     /**
9008      * @cfg {String} msgCls
9009      * The CSS class to apply to the loading message element (defaults to "x-mask-loading")
9010      */
9011     msgCls : 'x-mask-loading',
9012
9013     /**
9014      * Read-only. True if the mask is currently disabled so that it will not be displayed (defaults to false)
9015      * @type Boolean
9016      */
9017     disabled: false,
9018
9019     /**
9020      * Disables the mask to prevent it from being displayed
9021      */
9022     disable : function(){
9023        this.disabled = true;
9024     },
9025
9026     /**
9027      * Enables the mask so that it can be displayed
9028      */
9029     enable : function(){
9030         this.disabled = false;
9031     },
9032     
9033     onLoadException : function()
9034     {
9035         Roo.log(arguments);
9036         
9037         if (typeof(arguments[3]) != 'undefined') {
9038             Roo.MessageBox.alert("Error loading",arguments[3]);
9039         } 
9040         /*
9041         try {
9042             if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
9043                 Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
9044             }   
9045         } catch(e) {
9046             
9047         }
9048         */
9049     
9050         (function() { this.el.unmask(this.removeMask); }).defer(50, this);
9051     },
9052     // private
9053     onLoad : function()
9054     {
9055         (function() { this.el.unmask(this.removeMask); }).defer(50, this);
9056     },
9057
9058     // private
9059     onBeforeLoad : function(){
9060         if(!this.disabled){
9061             (function() { this.el.mask(this.msg, this.msgCls); }).defer(50, this);
9062         }
9063     },
9064
9065     // private
9066     destroy : function(){
9067         if(this.store){
9068             this.store.un('beforeload', this.onBeforeLoad, this);
9069             this.store.un('load', this.onLoad, this);
9070             this.store.un('loadexception', this.onLoadException, this);
9071         }else{
9072             var um = this.el.getUpdateManager();
9073             um.un('beforeupdate', this.onBeforeLoad, this);
9074             um.un('update', this.onLoad, this);
9075             um.un('failure', this.onLoad, this);
9076         }
9077     }
9078 };/**
9079  * @class Roo.bootstrap.Table
9080  * @licence LGBL
9081  * @extends Roo.bootstrap.Component
9082  * @children Roo.bootstrap.TableBody
9083  * Bootstrap Table class.  This class represents the primary interface of a component based grid control.
9084  * Similar to Roo.grid.Grid
9085  * <pre><code>
9086  var table = Roo.factory({
9087     xtype : 'Table',
9088     xns : Roo.bootstrap,
9089     autoSizeColumns: true,
9090     
9091     
9092     store : {
9093         xtype : 'Store',
9094         xns : Roo.data,
9095         remoteSort : true,
9096         sortInfo : { direction : 'ASC', field: 'name' },
9097         proxy : {
9098            xtype : 'HttpProxy',
9099            xns : Roo.data,
9100            method : 'GET',
9101            url : 'https://example.com/some.data.url.json'
9102         },
9103         reader : {
9104            xtype : 'JsonReader',
9105            xns : Roo.data,
9106            fields : [ 'id', 'name', whatever' ],
9107            id : 'id',
9108            root : 'data'
9109         }
9110     },
9111     cm : [
9112         {
9113             xtype : 'ColumnModel',
9114             xns : Roo.grid,
9115             align : 'center',
9116             cursor : 'pointer',
9117             dataIndex : 'is_in_group',
9118             header : "Name",
9119             sortable : true,
9120             renderer : function(v, x , r) {  
9121             
9122                 return String.format("{0}", v)
9123             }
9124             width : 3
9125         } // more columns..
9126     ],
9127     selModel : {
9128         xtype : 'RowSelectionModel',
9129         xns : Roo.bootstrap.Table
9130         // you can add listeners to catch selection change here....
9131     }
9132      
9133
9134  });
9135  // set any options
9136  grid.render(Roo.get("some-div"));
9137 </code></pre>
9138
9139 Currently the Table  uses multiple headers to try and handle XL / Medium etc... styling
9140
9141
9142
9143  *
9144  * @cfg {Roo.grid.AbstractSelectionModel} sm The selection model to use (cell selection is not supported yet)
9145  * @cfg {Roo.data.Store} store The data store to use
9146  * @cfg {Roo.grid.ColumnModel} cm[] A column for the grid.
9147  * 
9148  * @cfg {String} cls table class
9149  *
9150  *
9151  * @cfg {string} empty_results  Text to display for no results 
9152  * @cfg {boolean} striped Should the rows be alternative striped
9153  * @cfg {boolean} bordered Add borders to the table
9154  * @cfg {boolean} hover Add hover highlighting
9155  * @cfg {boolean} condensed Format condensed
9156  * @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,
9157  *                also adds table-responsive (see bootstrap docs for details)
9158  * @cfg {Boolean} loadMask (true|false) default false
9159  * @cfg {Boolean} footerShow (true|false) generate tfoot, default true
9160  * @cfg {Boolean} summaryFooterShow (true|false) generate tfoot for summary, default false
9161  * @cfg {Boolean} headerShow (true|false) generate thead, default true
9162  * @cfg {Boolean} rowSelection (true|false) default false
9163  * @cfg {Boolean} cellSelection (true|false) default false
9164  * @cfg {Boolean} scrollBody (true|false) default false - body scrolled / fixed header (with resizable columns)
9165  * @cfg {Roo.bootstrap.PagingToolbar} footer  a paging toolbar
9166  * @cfg {Boolean} lazyLoad  auto load data while scrolling to the end (default false)
9167  * @cfg {Boolean} auto_hide_footer  auto hide footer if only one page (default false)
9168  * @cfg {Boolean} enableColumnResize default true if columns can be resized = needs scrollBody to be set to work (drag/drop)
9169  * @cfg {Boolean} disableAutoSize disable autoSize() and initCSS()
9170  *
9171  * 
9172  * @cfg {Number} minColumnWidth default 50 pixels minimum column width 
9173  * 
9174  * @constructor
9175  * Create a new Table
9176  * @param {Object} config The config object
9177  */
9178
9179 Roo.bootstrap.Table = function(config)
9180 {
9181     Roo.bootstrap.Table.superclass.constructor.call(this, config);
9182      
9183     // BC...
9184     this.rowSelection = (typeof(config.rowSelection) != 'undefined') ? config.rowSelection : this.rowSelection;
9185     this.cellSelection = (typeof(config.cellSelection) != 'undefined') ? config.cellSelection : this.cellSelection;
9186     this.headerShow = (typeof(config.thead) != 'undefined') ? config.thead : this.headerShow;
9187     this.footerShow = (typeof(config.tfoot) != 'undefined') ? config.tfoot : this.footerShow;
9188     
9189     this.view = this; // compat with grid.
9190     
9191     this.sm = this.sm || {xtype: 'RowSelectionModel'};
9192     if (this.sm) {
9193         this.sm.grid = this;
9194         this.selModel = Roo.factory(this.sm, Roo.grid);
9195         this.sm = this.selModel;
9196         this.sm.xmodule = this.xmodule || false;
9197     }
9198     
9199     if (this.cm && typeof(this.cm.config) == 'undefined') {
9200         this.colModel = new Roo.grid.ColumnModel(this.cm);
9201         this.cm = this.colModel;
9202         this.cm.xmodule = this.xmodule || false;
9203     }
9204     if (this.store) {
9205         this.store= Roo.factory(this.store, Roo.data);
9206         this.ds = this.store;
9207         this.ds.xmodule = this.xmodule || false;
9208          
9209     }
9210     if (this.footer && this.store) {
9211         this.footer.dataSource = this.ds;
9212         this.footer = Roo.factory(this.footer);
9213     }
9214     
9215     /** @private */
9216     this.addEvents({
9217         /**
9218          * @event cellclick
9219          * Fires when a cell is clicked
9220          * @param {Roo.bootstrap.Table} this
9221          * @param {Roo.Element} el
9222          * @param {Number} rowIndex
9223          * @param {Number} columnIndex
9224          * @param {Roo.EventObject} e
9225          */
9226         "cellclick" : true,
9227         /**
9228          * @event celldblclick
9229          * Fires when a cell is double 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         "celldblclick" : true,
9237         /**
9238          * @event rowclick
9239          * Fires when a row is clicked
9240          * @param {Roo.bootstrap.Table} this
9241          * @param {Roo.Element} el
9242          * @param {Number} rowIndex
9243          * @param {Roo.EventObject} e
9244          */
9245         "rowclick" : true,
9246         /**
9247          * @event rowdblclick
9248          * Fires when a row is double clicked
9249          * @param {Roo.bootstrap.Table} this
9250          * @param {Roo.Element} el
9251          * @param {Number} rowIndex
9252          * @param {Roo.EventObject} e
9253          */
9254         "rowdblclick" : true,
9255         /**
9256          * @event mouseover
9257          * Fires when a mouseover occur
9258          * @param {Roo.bootstrap.Table} this
9259          * @param {Roo.Element} el
9260          * @param {Number} rowIndex
9261          * @param {Number} columnIndex
9262          * @param {Roo.EventObject} e
9263          */
9264         "mouseover" : true,
9265         /**
9266          * @event mouseout
9267          * Fires when a mouseout 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         "mouseout" : true,
9275         /**
9276          * @event rowclass
9277          * Fires when a row is rendered, so you can change add a style to it.
9278          * @param {Roo.bootstrap.Table} this
9279          * @param {Object} rowcfg   contains record  rowIndex colIndex and rowClass - set rowClass to add a style.
9280          */
9281         'rowclass' : true,
9282           /**
9283          * @event rowsrendered
9284          * Fires when all the  rows have been rendered
9285          * @param {Roo.bootstrap.Table} this
9286          */
9287         'rowsrendered' : true,
9288         /**
9289          * @event contextmenu
9290          * The raw contextmenu event for the entire grid.
9291          * @param {Roo.EventObject} e
9292          */
9293         "contextmenu" : true,
9294         /**
9295          * @event rowcontextmenu
9296          * Fires when a row is right clicked
9297          * @param {Roo.bootstrap.Table} this
9298          * @param {Number} rowIndex
9299          * @param {Roo.EventObject} e
9300          */
9301         "rowcontextmenu" : true,
9302         /**
9303          * @event cellcontextmenu
9304          * Fires when a cell is right clicked
9305          * @param {Roo.bootstrap.Table} this
9306          * @param {Number} rowIndex
9307          * @param {Number} cellIndex
9308          * @param {Roo.EventObject} e
9309          */
9310          "cellcontextmenu" : true,
9311          /**
9312          * @event headercontextmenu
9313          * Fires when a header is right clicked
9314          * @param {Roo.bootstrap.Table} this
9315          * @param {Number} columnIndex
9316          * @param {Roo.EventObject} e
9317          */
9318         "headercontextmenu" : true,
9319         /**
9320          * @event mousedown
9321          * The raw mousedown event for the entire grid.
9322          * @param {Roo.EventObject} e
9323          */
9324         "mousedown" : true
9325         
9326     });
9327 };
9328
9329 Roo.extend(Roo.bootstrap.Table, Roo.bootstrap.Component,  {
9330     
9331     cls: false,
9332     
9333     empty_results : '',
9334     striped : false,
9335     scrollBody : false,
9336     bordered: false,
9337     hover:  false,
9338     condensed : false,
9339     responsive : false,
9340     sm : false,
9341     cm : false,
9342     store : false,
9343     loadMask : false,
9344     footerShow : true,
9345     summaryFooterShow : false,
9346     headerShow : true,
9347     enableColumnResize: true,
9348     disableAutoSize: false,
9349   
9350     rowSelection : false,
9351     cellSelection : false,
9352     layout : false,
9353
9354     minColumnWidth : 50,
9355     
9356     // Roo.Element - the tbody
9357     bodyEl: false,  // <tbody> Roo.Element - thead element    
9358     headEl: false,  // <thead> Roo.Element - thead element
9359     resizeProxy : false, // proxy element for dragging?
9360
9361
9362     
9363     container: false, // used by gridpanel...
9364     
9365     lazyLoad : false,
9366     
9367     CSS : Roo.util.CSS,
9368     
9369     auto_hide_footer : false,
9370     
9371     view: false, // actually points to this..
9372     
9373     getAutoCreate : function()
9374     {
9375         var cfg = Roo.apply({}, Roo.bootstrap.Table.superclass.getAutoCreate.call(this));
9376         
9377         cfg = {
9378             tag: 'table',
9379             cls : 'table', 
9380             cn : []
9381         };
9382         // this get's auto added by panel.Grid
9383         if (this.scrollBody) {
9384             cfg.cls += ' table-body-fixed';
9385         }    
9386         if (this.striped) {
9387             cfg.cls += ' table-striped';
9388         }
9389         
9390         if (this.hover) {
9391             cfg.cls += ' table-hover';
9392         }
9393         if (this.bordered) {
9394             cfg.cls += ' table-bordered';
9395         }
9396         if (this.condensed) {
9397             cfg.cls += ' table-condensed';
9398         }
9399         
9400         if (this.responsive) {
9401             cfg.cls += ' table-responsive';
9402         }
9403         
9404         if (this.cls) {
9405             cfg.cls+=  ' ' +this.cls;
9406         }
9407         
9408         
9409         
9410         if (this.layout) {
9411             cfg.style = (typeof(cfg.style) == 'undefined') ? ('table-layout:' + this.layout + ';') : (cfg.style + ('table-layout:' + this.layout + ';'));
9412         }
9413         
9414         if(this.store || this.cm){
9415             if(this.headerShow){
9416                 cfg.cn.push(this.renderHeader());
9417             }
9418             
9419             cfg.cn.push(this.renderBody());
9420             
9421             if(this.footerShow){
9422                 cfg.cn.push(this.renderFooter());
9423             }
9424
9425             if(!this.footerShow && this.summaryFooterShow) {
9426                 cfg.cn.push(this.renderSummaryFooter());
9427             }
9428
9429             // where does this come from?
9430             //cfg.cls+=  ' TableGrid';
9431         }
9432         
9433         return { cn : [ cfg ] };
9434     },
9435     
9436     initEvents : function()
9437     {   
9438         if(!this.store || !this.cm){
9439             return;
9440         }
9441         if (this.selModel) {
9442             this.selModel.initEvents();
9443         }
9444         
9445         
9446         //Roo.log('initEvents with ds!!!!');
9447         
9448         this.bodyEl = this.el.select('tbody', true).first();
9449         this.headEl = this.el.select('thead', true).first();
9450         this.mainFoot = this.el.select('tfoot', true).first();
9451         
9452         
9453         
9454         
9455         Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
9456             e.on('click', this.sort, this);
9457         }, this);
9458         
9459         
9460         // why is this done????? = it breaks dialogs??
9461         //this.parent().el.setStyle('position', 'relative');
9462         
9463         
9464         if (this.footer) {
9465             this.footer.parentId = this.id;
9466             this.footer.onRender(this.el.select('tfoot tr td').first(), null);
9467             
9468             if(this.lazyLoad){
9469                 this.el.select('tfoot tr td').first().addClass('hide');
9470             }
9471         } 
9472         
9473         if(this.loadMask) {
9474             this.maskEl = new Roo.LoadMask(this.el, { store : this.ds, msgCls: 'roo-el-mask-msg' });
9475         }
9476         
9477         this.store.on('load', this.onLoad, this);
9478         this.store.on('beforeload', this.onBeforeLoad, this);
9479         this.store.on('update', this.onUpdate, this);
9480         this.store.on('add', this.onAdd, this);
9481         this.store.on("clear", this.clear, this);
9482         
9483         this.el.on("contextmenu", this.onContextMenu, this);
9484         
9485         
9486         this.cm.on("headerchange", this.onHeaderChange, this);
9487         this.cm.on("hiddenchange", this.onHiddenChange, this, arguments);
9488
9489  //?? does bodyEl get replaced on render?
9490         this.bodyEl.on("click", this.onClick, this);
9491         this.bodyEl.on("dblclick", this.onDblClick, this);        
9492         this.bodyEl.on('scroll', this.onBodyScroll, this);
9493
9494         // guessing mainbody will work - this relays usually caught by selmodel at present.
9495         this.relayEvents(this.bodyEl, ["mousedown","mouseup","mouseover","mouseout","keypress"]);
9496   
9497   
9498         this.resizeProxy = Roo.get(document.body).createChild({ cls:"x-grid-resize-proxy", html: '&#160;' });
9499         
9500   
9501         if(this.headEl && this.enableColumnResize !== false && Roo.grid.SplitDragZone){
9502             new Roo.grid.SplitDragZone(this, this.headEl.dom, false); // not sure what 'lockedHd is for this implementation..)
9503         }
9504         
9505         this.initCSS();
9506     },
9507     // Compatibility with grid - we implement all the view features at present.
9508     getView : function()
9509     {
9510         return this;
9511     },
9512     
9513     initCSS : function()
9514     {
9515         if(this.disableAutoSize) {
9516             return;
9517         }
9518         
9519         var cm = this.cm, styles = [];
9520         this.CSS.removeStyleSheet(this.id + '-cssrules');
9521         var headHeight = this.headEl ? this.headEl.dom.clientHeight : 0;
9522         // we can honour xs/sm/md/xl  as widths...
9523         // we first have to decide what widht we are currently at...
9524         var sz = Roo.getGridSize();
9525         
9526         var total = 0;
9527         var last = -1;
9528         var cols = []; // visable cols.
9529         var total_abs = 0;
9530         for(var i = 0, len = cm.getColumnCount(); i < len; i++) {
9531             var w = cm.getColumnWidth(i, false);
9532             if(cm.isHidden(i)){
9533                 cols.push( { rel : false, abs : 0 });
9534                 continue;
9535             }
9536             if (w !== false) {
9537                 cols.push( { rel : false, abs : w });
9538                 total_abs += w;
9539                 last = i; // not really..
9540                 continue;
9541             }
9542             var w = cm.getColumnWidth(i, sz);
9543             if (w > 0) {
9544                 last = i
9545             }
9546             total += w;
9547             cols.push( { rel : w, abs : false });
9548         }
9549         
9550         var avail = this.bodyEl.dom.clientWidth - total_abs;
9551         
9552         var unitWidth = Math.floor(avail / total);
9553         var rem = avail - (unitWidth * total);
9554         
9555         var hidden, width, pos = 0 , splithide , left;
9556         for(var i = 0, len = cm.getColumnCount(); i < len; i++) {
9557             
9558             hidden = 'display:none;';
9559             left = '';
9560             width  = 'width:0px;';
9561             splithide = '';
9562             if(!cm.isHidden(i)){
9563                 hidden = '';
9564                 
9565                 
9566                 // we can honour xs/sm/md/xl ?
9567                 var w = cols[i].rel == false ? cols[i].abs : (cols[i].rel * unitWidth);
9568                 if (w===0) {
9569                     hidden = 'display:none;';
9570                 }
9571                 // width should return a small number...
9572                 if (i == last) {
9573                     w+=rem; // add the remaining with..
9574                 }
9575                 pos += w;
9576                 left = "left:" + (pos -4) + "px;";
9577                 width = "width:" + w+ "px;";
9578                 
9579             }
9580             if (this.responsive) {
9581                 width = '';
9582                 left = '';
9583                 hidden = cm.isHidden(i) ? 'display:none;' : '';
9584                 splithide = 'display: none;';
9585             }
9586             
9587             styles.push( '#' , this.id , ' .x-col-' , i, " {", cm.config[i].css, width, hidden, "}\n" );
9588             if (this.headEl) {
9589                 if (i == last) {
9590                     splithide = 'display:none;';
9591                 }
9592                 
9593                 styles.push('#' , this.id , ' .x-hcol-' , i, " { ", width, hidden," }\n",
9594                             '#' , this.id , ' .x-grid-split-' , i, " { ", left, splithide, 'height:', (headHeight - 4), "px;}\n",
9595                             // this is the popover version..
9596                             '.popover-inner #' , this.id , ' .x-grid-split-' , i, " { ", left, splithide, 'height:', 100, "%;}\n"
9597                 );
9598             }
9599             
9600         }
9601         //Roo.log(styles.join(''));
9602         this.CSS.createStyleSheet( styles.join(''), this.id + '-cssrules');
9603         
9604     },
9605     
9606     
9607     
9608     onContextMenu : function(e, t)
9609     {
9610         this.processEvent("contextmenu", e);
9611     },
9612     
9613     processEvent : function(name, e)
9614     {
9615         if (name != 'touchstart' ) {
9616             this.fireEvent(name, e);    
9617         }
9618         
9619         var t = e.getTarget();
9620         
9621         var cell = Roo.get(t);
9622         
9623         if(!cell){
9624             return;
9625         }
9626         
9627         if(cell.findParent('tfoot', false, true)){
9628             return;
9629         }
9630         
9631         if(cell.findParent('thead', false, true)){
9632             
9633             if(e.getTarget().nodeName.toLowerCase() != 'th'){
9634                 cell = Roo.get(t).findParent('th', false, true);
9635                 if (!cell) {
9636                     Roo.log("failed to find th in thead?");
9637                     Roo.log(e.getTarget());
9638                     return;
9639                 }
9640             }
9641             
9642             var cellIndex = cell.dom.cellIndex;
9643             
9644             var ename = name == 'touchstart' ? 'click' : name;
9645             this.fireEvent("header" + ename, this, cellIndex, e);
9646             
9647             return;
9648         }
9649         
9650         if(e.getTarget().nodeName.toLowerCase() != 'td'){
9651             cell = Roo.get(t).findParent('td', false, true);
9652             if (!cell) {
9653                 Roo.log("failed to find th in tbody?");
9654                 Roo.log(e.getTarget());
9655                 return;
9656             }
9657         }
9658         
9659         var row = cell.findParent('tr', false, true);
9660         var cellIndex = cell.dom.cellIndex;
9661         var rowIndex = row.dom.rowIndex - 1;
9662         
9663         if(row !== false){
9664             
9665             this.fireEvent("row" + name, this, rowIndex, e);
9666             
9667             if(cell !== false){
9668             
9669                 this.fireEvent("cell" + name, this, rowIndex, cellIndex, e);
9670             }
9671         }
9672         
9673     },
9674     
9675     onMouseover : function(e, el)
9676     {
9677         var cell = Roo.get(el);
9678         
9679         if(!cell){
9680             return;
9681         }
9682         
9683         if(e.getTarget().nodeName.toLowerCase() != 'td'){
9684             cell = cell.findParent('td', false, true);
9685         }
9686         
9687         var row = cell.findParent('tr', false, true);
9688         var cellIndex = cell.dom.cellIndex;
9689         var rowIndex = row.dom.rowIndex - 1; // start from 0
9690         
9691         this.fireEvent('mouseover', this, cell, rowIndex, cellIndex, e);
9692         
9693     },
9694     
9695     onMouseout : function(e, el)
9696     {
9697         var cell = Roo.get(el);
9698         
9699         if(!cell){
9700             return;
9701         }
9702         
9703         if(e.getTarget().nodeName.toLowerCase() != 'td'){
9704             cell = cell.findParent('td', false, true);
9705         }
9706         
9707         var row = cell.findParent('tr', false, true);
9708         var cellIndex = cell.dom.cellIndex;
9709         var rowIndex = row.dom.rowIndex - 1; // start from 0
9710         
9711         this.fireEvent('mouseout', this, cell, rowIndex, cellIndex, e);
9712         
9713     },
9714     
9715     onClick : function(e, el)
9716     {
9717         var cell = Roo.get(el);
9718         
9719         if(!cell || (!this.cellSelection && !this.rowSelection)){
9720             return;
9721         }
9722         
9723         if(e.getTarget().nodeName.toLowerCase() != 'td'){
9724             cell = cell.findParent('td', false, true);
9725         }
9726         
9727         if(!cell || typeof(cell) == 'undefined'){
9728             return;
9729         }
9730         
9731         var row = cell.findParent('tr', false, true);
9732         
9733         if(!row || typeof(row) == 'undefined'){
9734             return;
9735         }
9736         
9737         var cellIndex = cell.dom.cellIndex;
9738         var rowIndex = this.getRowIndex(row);
9739         
9740         // why??? - should these not be based on SelectionModel?
9741         //if(this.cellSelection){
9742             this.fireEvent('cellclick', this, cell, rowIndex, cellIndex, e);
9743         //}
9744         
9745         //if(this.rowSelection){
9746             this.fireEvent('rowclick', this, row, rowIndex, e);
9747         //}
9748          
9749     },
9750         
9751     onDblClick : function(e,el)
9752     {
9753         var cell = Roo.get(el);
9754         
9755         if(!cell || (!this.cellSelection && !this.rowSelection)){
9756             return;
9757         }
9758         
9759         if(e.getTarget().nodeName.toLowerCase() != 'td'){
9760             cell = cell.findParent('td', false, true);
9761         }
9762         
9763         if(!cell || typeof(cell) == 'undefined'){
9764             return;
9765         }
9766         
9767         var row = cell.findParent('tr', false, true);
9768         
9769         if(!row || typeof(row) == 'undefined'){
9770             return;
9771         }
9772         
9773         var cellIndex = cell.dom.cellIndex;
9774         var rowIndex = this.getRowIndex(row);
9775         
9776         if(this.cellSelection){
9777             this.fireEvent('celldblclick', this, cell, rowIndex, cellIndex, e);
9778         }
9779         
9780         if(this.rowSelection){
9781             this.fireEvent('rowdblclick', this, row, rowIndex, e);
9782         }
9783     },
9784     findRowIndex : function(el)
9785     {
9786         var cell = Roo.get(el);
9787         if(!cell) {
9788             return false;
9789         }
9790         var row = cell.findParent('tr', false, true);
9791         
9792         if(!row || typeof(row) == 'undefined'){
9793             return false;
9794         }
9795         return this.getRowIndex(row);
9796     },
9797     sort : function(e,el)
9798     {
9799         var col = Roo.get(el);
9800         
9801         if(!col.hasClass('sortable')){
9802             return;
9803         }
9804         
9805         var sort = col.attr('sort');
9806         var dir = 'ASC';
9807         
9808         if(col.select('i', true).first().hasClass('fa-arrow-up')){
9809             dir = 'DESC';
9810         }
9811         
9812         this.store.sortInfo = {field : sort, direction : dir};
9813         
9814         if (this.footer) {
9815             Roo.log("calling footer first");
9816             this.footer.onClick('first');
9817         } else {
9818         
9819             this.store.load({ params : { start : 0 } });
9820         }
9821     },
9822     
9823     renderHeader : function()
9824     {
9825         var header = {
9826             tag: 'thead',
9827             cn : []
9828         };
9829         
9830         var cm = this.cm;
9831         this.totalWidth = 0;
9832         
9833         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
9834             
9835             var config = cm.config[i];
9836             
9837             var c = {
9838                 tag: 'th',
9839                 cls : 'x-hcol-' + i,
9840                 style : '',
9841                 
9842                 html: cm.getColumnHeader(i)
9843             };
9844             
9845             var tooltip = cm.getColumnTooltip(i);
9846             if (tooltip) {
9847                 c.tooltip = tooltip;
9848             }
9849             
9850             
9851             var hh = '';
9852             
9853             if(typeof(config.sortable) != 'undefined' && config.sortable){
9854                 c.cls += ' sortable';
9855                 c.html = '<i class="fa"></i>' + c.html;
9856             }
9857             
9858             // could use BS4 hidden-..-down 
9859             
9860             if(typeof(config.lgHeader) != 'undefined'){
9861                 hh += '<span class="hidden-xs hidden-sm hidden-md ">' + config.lgHeader + '</span>';
9862             }
9863             
9864             if(typeof(config.mdHeader) != 'undefined'){
9865                 hh += '<span class="hidden-xs hidden-sm hidden-lg">' + config.mdHeader + '</span>';
9866             }
9867             
9868             if(typeof(config.smHeader) != 'undefined'){
9869                 hh += '<span class="hidden-xs hidden-md hidden-lg">' + config.smHeader + '</span>';
9870             }
9871             
9872             if(typeof(config.xsHeader) != 'undefined'){
9873                 hh += '<span class="hidden-sm hidden-md hidden-lg">' + config.xsHeader + '</span>';
9874             }
9875             
9876             if(hh.length){
9877                 c.html = hh;
9878             }
9879             
9880             if(typeof(config.tooltip) != 'undefined'){
9881                 c.tooltip = config.tooltip;
9882             }
9883             
9884             if(typeof(config.colspan) != 'undefined'){
9885                 c.colspan = config.colspan;
9886             }
9887             
9888             // hidden is handled by CSS now
9889             
9890             if(typeof(config.dataIndex) != 'undefined'){
9891                 c.sort = config.dataIndex;
9892             }
9893             
9894            
9895             
9896             if(typeof(config.align) != 'undefined' && config.align.length){
9897                 c.style += ' text-align:' + config.align + ';';
9898             }
9899             
9900             /* width is done in CSS
9901              *if(typeof(config.width) != 'undefined'){
9902                 c.style += ' width:' + config.width + 'px;';
9903                 this.totalWidth += config.width;
9904             } else {
9905                 this.totalWidth += 100; // assume minimum of 100 per column?
9906             }
9907             */
9908             
9909             if(typeof(config.cls) != 'undefined'){
9910                 c.cls = (typeof(c.cls) == 'undefined') ? config.cls : (c.cls + ' ' + config.cls);
9911             }
9912             // this is the bit that doesnt reall work at all...
9913             
9914             if (this.responsive) {
9915                  
9916             
9917                 ['xs','sm','md','lg'].map(function(size){
9918                     
9919                     if(typeof(config[size]) == 'undefined'){
9920                         return;
9921                     }
9922                      
9923                     if (!config[size]) { // 0 = hidden
9924                         // BS 4 '0' is treated as hide that column and below.
9925                         c.cls += ' hidden-' + size + ' hidden' + size + '-down';
9926                         return;
9927                     }
9928                     
9929                     c.cls += ' col-' + size + '-' + config[size] + (
9930                         size == 'xs' ? (' col-' + config[size] ) : '' // bs4 col-{num} replaces col-xs
9931                     );
9932                     
9933                     
9934                 });
9935             }
9936             // at the end?
9937             
9938             c.html +=' <span class="x-grid-split x-grid-split-' + i + '"></span>';
9939             
9940             
9941             
9942             
9943             header.cn.push(c)
9944         }
9945         
9946         return header;
9947     },
9948     
9949     renderBody : function()
9950     {
9951         var body = {
9952             tag: 'tbody',
9953             cn : [
9954                 {
9955                     tag: 'tr',
9956                     cn : [
9957                         {
9958                             tag : 'td',
9959                             colspan :  this.cm.getColumnCount()
9960                         }
9961                     ]
9962                 }
9963             ]
9964         };
9965         
9966         return body;
9967     },
9968     
9969     renderFooter : function()
9970     {
9971         var footer = {
9972             tag: 'tfoot',
9973             cn : [
9974                 {
9975                     tag: 'tr',
9976                     cn : [
9977                         {
9978                             tag : 'td',
9979                             colspan :  this.cm.getColumnCount()
9980                         }
9981                     ]
9982                 }
9983             ]
9984         };
9985         
9986         return footer;
9987     },
9988
9989     renderSummaryFooter : function()
9990     {
9991         var footer = {
9992             tag: 'tfoot',
9993             cn : []
9994         };
9995
9996         var cm = this.cm;
9997         
9998         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
9999             
10000             var c = {
10001                 tag: 'td',
10002                 cls : 'x-fcol-' + i,
10003                 style : '',
10004                 html: ''
10005             };
10006             
10007             footer.cn.push(c)
10008         }
10009         
10010         return footer;
10011     },
10012     
10013     
10014     
10015     onLoad : function()
10016     {
10017 //        Roo.log('ds onload');
10018         this.clear();
10019         
10020         var _this = this;
10021         var cm = this.cm;
10022         var ds = this.store;
10023         
10024         Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
10025             e.select('i', true).removeClass(['fa-arrow-up', 'fa-arrow-down']);
10026             if (_this.store.sortInfo) {
10027                     
10028                 if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'ASC'){
10029                     e.select('i', true).addClass(['fa-arrow-up']);
10030                 }
10031                 
10032                 if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'DESC'){
10033                     e.select('i', true).addClass(['fa-arrow-down']);
10034                 }
10035             }
10036         });
10037         
10038         var tbody =  this.bodyEl;
10039               
10040         if(ds.getCount() > 0){
10041             ds.data.each(function(d,rowIndex){
10042                 var row =  this.renderRow(cm, ds, rowIndex);
10043                 
10044                 tbody.createChild(row);
10045                 
10046                 var _this = this;
10047                 
10048                 if(row.cellObjects.length){
10049                     Roo.each(row.cellObjects, function(r){
10050                         _this.renderCellObject(r);
10051                     })
10052                 }
10053                 
10054             }, this);
10055         } else if (this.empty_results.length) {
10056             this.el.mask(this.empty_results, 'no-spinner');
10057         }
10058         
10059         var tfoot = this.el.select('tfoot', true).first();
10060         
10061         if(this.footerShow && this.auto_hide_footer && this.mainFoot){
10062             
10063             this.mainFoot.setVisibilityMode(Roo.Element.DISPLAY).hide();
10064             
10065             var total = this.ds.getTotalCount();
10066             
10067             if(this.footer.pageSize < total){
10068                 this.mainFoot.show();
10069             }
10070         }
10071
10072         if(!this.footerShow && this.summaryFooterShow) {
10073
10074             for(var i = 0, len = cm.getColumnCount(); i < len; i++){
10075         
10076                 var value = cm.config[i].summaryFooter;
10077
10078                 Roo.log('value [' + i + '] : ' + value);
10079             }
10080         }
10081         
10082         Roo.each(this.el.select('tbody td', true).elements, function(e){
10083             e.on('mouseover', _this.onMouseover, _this);
10084         });
10085         
10086         Roo.each(this.el.select('tbody td', true).elements, function(e){
10087             e.on('mouseout', _this.onMouseout, _this);
10088         });
10089         this.fireEvent('rowsrendered', this);
10090         
10091         this.autoSize();
10092         
10093         this.initCSS(); /// resize cols
10094
10095         
10096     },
10097     
10098     
10099     onUpdate : function(ds,record)
10100     {
10101         this.refreshRow(record);
10102         this.autoSize();
10103     },
10104     
10105     onRemove : function(ds, record, index, isUpdate){
10106         if(isUpdate !== true){
10107             this.fireEvent("beforerowremoved", this, index, record);
10108         }
10109         var bt = this.bodyEl.dom;
10110         
10111         var rows = this.el.select('tbody > tr', true).elements;
10112         
10113         if(typeof(rows[index]) != 'undefined'){
10114             bt.removeChild(rows[index].dom);
10115         }
10116         
10117 //        if(bt.rows[index]){
10118 //            bt.removeChild(bt.rows[index]);
10119 //        }
10120         
10121         if(isUpdate !== true){
10122             //this.stripeRows(index);
10123             //this.syncRowHeights(index, index);
10124             //this.layout();
10125             this.fireEvent("rowremoved", this, index, record);
10126         }
10127     },
10128     
10129     onAdd : function(ds, records, rowIndex)
10130     {
10131         //Roo.log('on Add called');
10132         // - note this does not handle multiple adding very well..
10133         var bt = this.bodyEl.dom;
10134         for (var i =0 ; i < records.length;i++) {
10135             //Roo.log('call insert row Add called on ' + rowIndex + ':' + i);
10136             //Roo.log(records[i]);
10137             //Roo.log(this.store.getAt(rowIndex+i));
10138             this.insertRow(this.store, rowIndex + i, false);
10139             return;
10140         }
10141         
10142     },
10143     
10144     
10145     refreshRow : function(record){
10146         var ds = this.store, index;
10147         if(typeof record == 'number'){
10148             index = record;
10149             record = ds.getAt(index);
10150         }else{
10151             index = ds.indexOf(record);
10152             if (index < 0) {
10153                 return; // should not happen - but seems to 
10154             }
10155         }
10156         this.insertRow(ds, index, true);
10157         this.autoSize();
10158         this.onRemove(ds, record, index+1, true);
10159         this.autoSize();
10160         //this.syncRowHeights(index, index);
10161         //this.layout();
10162         this.fireEvent("rowupdated", this, index, record);
10163     },
10164     // private - called by RowSelection
10165     onRowSelect : function(rowIndex){
10166         var row = this.getRowDom(rowIndex);
10167         row.addClass(['bg-info','info']);
10168     },
10169     // private - called by RowSelection
10170     onRowDeselect : function(rowIndex)
10171     {
10172         if (rowIndex < 0) {
10173             return;
10174         }
10175         var row = this.getRowDom(rowIndex);
10176         row.removeClass(['bg-info','info']);
10177     },
10178       /**
10179      * Focuses the specified row.
10180      * @param {Number} row The row index
10181      */
10182     focusRow : function(row)
10183     {
10184         //Roo.log('GridView.focusRow');
10185         var x = this.bodyEl.dom.scrollLeft;
10186         this.focusCell(row, 0, false);
10187         this.bodyEl.dom.scrollLeft = x;
10188
10189     },
10190      /**
10191      * Focuses the specified cell.
10192      * @param {Number} row The row index
10193      * @param {Number} col The column index
10194      * @param {Boolean} hscroll false to disable horizontal scrolling
10195      */
10196     focusCell : function(row, col, hscroll)
10197     {
10198         //Roo.log('GridView.focusCell');
10199         var el = this.ensureVisible(row, col, hscroll);
10200         // not sure what focusEL achives = it's a <a> pos relative 
10201         //this.focusEl.alignTo(el, "tl-tl");
10202         //if(Roo.isGecko){
10203         //    this.focusEl.focus();
10204         //}else{
10205         //    this.focusEl.focus.defer(1, this.focusEl);
10206         //}
10207     },
10208     
10209      /**
10210      * Scrolls the specified cell into view
10211      * @param {Number} row The row index
10212      * @param {Number} col The column index
10213      * @param {Boolean} hscroll false to disable horizontal scrolling
10214      */
10215     ensureVisible : function(row, col, hscroll)
10216     {
10217         //Roo.log('GridView.ensureVisible,' + row + ',' + col);
10218         //return null; //disable for testing.
10219         if(typeof row != "number"){
10220             row = row.rowIndex;
10221         }
10222         if(row < 0 && row >= this.ds.getCount()){
10223             return  null;
10224         }
10225         col = (col !== undefined ? col : 0);
10226         var cm = this.cm;
10227         while(cm.isHidden(col)){
10228             col++;
10229         }
10230
10231         var el = this.getCellDom(row, col);
10232         if(!el){
10233             return null;
10234         }
10235         var c = this.bodyEl.dom;
10236
10237         var ctop = parseInt(el.offsetTop, 10);
10238         var cleft = parseInt(el.offsetLeft, 10);
10239         var cbot = ctop + el.offsetHeight;
10240         var cright = cleft + el.offsetWidth;
10241
10242         //var ch = c.clientHeight - this.mainHd.dom.offsetHeight;
10243         var ch = 0; //?? header is not withing the area?
10244         var stop = parseInt(c.scrollTop, 10);
10245         var sleft = parseInt(c.scrollLeft, 10);
10246         var sbot = stop + ch;
10247         var sright = sleft + c.clientWidth;
10248         /*
10249         Roo.log('GridView.ensureVisible:' +
10250                 ' ctop:' + ctop +
10251                 ' c.clientHeight:' + c.clientHeight +
10252                 ' this.mainHd.dom.offsetHeight:' + this.mainHd.dom.offsetHeight +
10253                 ' stop:' + stop +
10254                 ' cbot:' + cbot +
10255                 ' sbot:' + sbot +
10256                 ' ch:' + ch  
10257                 );
10258         */
10259         if(ctop < stop){
10260             c.scrollTop = ctop;
10261             //Roo.log("set scrolltop to ctop DISABLE?");
10262         }else if(cbot > sbot){
10263             //Roo.log("set scrolltop to cbot-ch");
10264             c.scrollTop = cbot-ch;
10265         }
10266
10267         if(hscroll !== false){
10268             if(cleft < sleft){
10269                 c.scrollLeft = cleft;
10270             }else if(cright > sright){
10271                 c.scrollLeft = cright-c.clientWidth;
10272             }
10273         }
10274
10275         return el;
10276     },
10277     
10278     
10279     insertRow : function(dm, rowIndex, isUpdate){
10280         
10281         if(!isUpdate){
10282             this.fireEvent("beforerowsinserted", this, rowIndex);
10283         }
10284             //var s = this.getScrollState();
10285         var row = this.renderRow(this.cm, this.store, rowIndex);
10286         // insert before rowIndex..
10287         var e = this.bodyEl.createChild(row,this.getRowDom(rowIndex));
10288         
10289         var _this = this;
10290                 
10291         if(row.cellObjects.length){
10292             Roo.each(row.cellObjects, function(r){
10293                 _this.renderCellObject(r);
10294             })
10295         }
10296             
10297         if(!isUpdate){
10298             this.fireEvent("rowsinserted", this, rowIndex);
10299             //this.syncRowHeights(firstRow, lastRow);
10300             //this.stripeRows(firstRow);
10301             //this.layout();
10302         }
10303         
10304     },
10305     
10306     
10307     getRowDom : function(rowIndex)
10308     {
10309         var rows = this.el.select('tbody > tr', true).elements;
10310         
10311         return (typeof(rows[rowIndex]) == 'undefined') ? false : rows[rowIndex];
10312         
10313     },
10314     getCellDom : function(rowIndex, colIndex)
10315     {
10316         var row = this.getRowDom(rowIndex);
10317         if (row === false) {
10318             return false;
10319         }
10320         var cols = row.select('td', true).elements;
10321         return (typeof(cols[colIndex]) == 'undefined') ? false : cols[colIndex];
10322         
10323     },
10324     
10325     // returns the object tree for a tr..
10326   
10327     
10328     renderRow : function(cm, ds, rowIndex) 
10329     {
10330         var d = ds.getAt(rowIndex);
10331         
10332         var row = {
10333             tag : 'tr',
10334             cls : 'x-row-' + rowIndex,
10335             cn : []
10336         };
10337             
10338         var cellObjects = [];
10339         
10340         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
10341             var config = cm.config[i];
10342             
10343             var renderer = cm.getRenderer(i);
10344             var value = '';
10345             var id = false;
10346             
10347             if(typeof(renderer) !== 'undefined'){
10348                 value = renderer(d.data[cm.getDataIndex(i)], false, d);
10349             }
10350             // if object are returned, then they are expected to be Roo.bootstrap.Component instances
10351             // and are rendered into the cells after the row is rendered - using the id for the element.
10352             
10353             if(typeof(value) === 'object'){
10354                 id = Roo.id();
10355                 cellObjects.push({
10356                     container : id,
10357                     cfg : value 
10358                 })
10359             }
10360             
10361             var rowcfg = {
10362                 record: d,
10363                 rowIndex : rowIndex,
10364                 colIndex : i,
10365                 rowClass : ''
10366             };
10367
10368             this.fireEvent('rowclass', this, rowcfg);
10369             
10370             var td = {
10371                 tag: 'td',
10372                 // this might end up displaying HTML?
10373                 // this is too messy... - better to only do it on columsn you know are going to be too long
10374                 //tooltip : (typeof(value) === 'object') ? '' : value,
10375                 cls : rowcfg.rowClass + ' x-col-' + i,
10376                 style: '',
10377                 html: (typeof(value) === 'object') ? '' : value
10378             };
10379             
10380             if (id) {
10381                 td.id = id;
10382             }
10383             
10384             if(typeof(config.colspan) != 'undefined'){
10385                 td.colspan = config.colspan;
10386             }
10387             
10388             
10389             
10390             if(typeof(config.align) != 'undefined' && config.align.length){
10391                 td.style += ' text-align:' + config.align + ';';
10392             }
10393             if(typeof(config.valign) != 'undefined' && config.valign.length){
10394                 td.style += ' vertical-align:' + config.valign + ';';
10395             }
10396             /*
10397             if(typeof(config.width) != 'undefined'){
10398                 td.style += ' width:' +  config.width + 'px;';
10399             }
10400             */
10401             
10402             if(typeof(config.cursor) != 'undefined'){
10403                 td.style += ' cursor:' +  config.cursor + ';';
10404             }
10405             
10406             if(typeof(config.cls) != 'undefined'){
10407                 td.cls = (typeof(td.cls) == 'undefined') ? config.cls : (td.cls + ' ' + config.cls);
10408             }
10409             if (this.responsive) {
10410                 ['xs','sm','md','lg'].map(function(size){
10411                     
10412                     if(typeof(config[size]) == 'undefined'){
10413                         return;
10414                     }
10415                     
10416                     
10417                       
10418                     if (!config[size]) { // 0 = hidden
10419                         // BS 4 '0' is treated as hide that column and below.
10420                         td.cls += ' hidden-' + size + ' hidden' + size + '-down';
10421                         return;
10422                     }
10423                     
10424                     td.cls += ' col-' + size + '-' + config[size] + (
10425                         size == 'xs' ? (' col-' +   config[size] ) : '' // bs4 col-{num} replaces col-xs
10426                     );
10427                      
10428     
10429                 });
10430             }
10431             row.cn.push(td);
10432            
10433         }
10434         
10435         row.cellObjects = cellObjects;
10436         
10437         return row;
10438           
10439     },
10440     
10441     
10442     
10443     onBeforeLoad : function()
10444     {
10445         this.el.unmask(); // if needed.
10446     },
10447      /**
10448      * Remove all rows
10449      */
10450     clear : function()
10451     {
10452         this.el.select('tbody', true).first().dom.innerHTML = '';
10453     },
10454     /**
10455      * Show or hide a row.
10456      * @param {Number} rowIndex to show or hide
10457      * @param {Boolean} state hide
10458      */
10459     setRowVisibility : function(rowIndex, state)
10460     {
10461         var bt = this.bodyEl.dom;
10462         
10463         var rows = this.el.select('tbody > tr', true).elements;
10464         
10465         if(typeof(rows[rowIndex]) == 'undefined'){
10466             return;
10467         }
10468         rows[rowIndex][ state ? 'removeClass' : 'addClass']('d-none');
10469         
10470     },
10471     
10472     
10473     getSelectionModel : function(){
10474         if(!this.selModel){
10475             this.selModel = new Roo.bootstrap.Table.RowSelectionModel({grid: this});
10476         }
10477         return this.selModel;
10478     },
10479     /*
10480      * Render the Roo.bootstrap object from renderder
10481      */
10482     renderCellObject : function(r)
10483     {
10484         var _this = this;
10485         
10486         r.cfg.parentId = (typeof(r.container) == 'string') ? r.container : r.container.id;
10487         
10488         var t = r.cfg.render(r.container);
10489         
10490         if(r.cfg.cn){
10491             Roo.each(r.cfg.cn, function(c){
10492                 var child = {
10493                     container: t.getChildContainer(),
10494                     cfg: c
10495                 };
10496                 _this.renderCellObject(child);
10497             })
10498         }
10499     },
10500     /**
10501      * get the Row Index from a dom element.
10502      * @param {Roo.Element} row The row to look for
10503      * @returns {Number} the row
10504      */
10505     getRowIndex : function(row)
10506     {
10507         var rowIndex = -1;
10508         
10509         Roo.each(this.el.select('tbody > tr', true).elements, function(el, index){
10510             if(el != row){
10511                 return;
10512             }
10513             
10514             rowIndex = index;
10515         });
10516         
10517         return rowIndex;
10518     },
10519     /**
10520      * get the header TH element for columnIndex
10521      * @param {Number} columnIndex
10522      * @returns {Roo.Element}
10523      */
10524     getHeaderIndex: function(colIndex)
10525     {
10526         var cols = this.headEl.select('th', true).elements;
10527         return cols[colIndex]; 
10528     },
10529     /**
10530      * get the Column Index from a dom element. (using regex on x-hcol-{colid})
10531      * @param {domElement} cell to look for
10532      * @returns {Number} the column
10533      */
10534     getCellIndex : function(cell)
10535     {
10536         var id = String(cell.className).match(Roo.bootstrap.Table.cellRE);
10537         if(id){
10538             return parseInt(id[1], 10);
10539         }
10540         return 0;
10541     },
10542      /**
10543      * Returns the grid's underlying element = used by panel.Grid
10544      * @return {Element} The element
10545      */
10546     getGridEl : function(){
10547         return this.el;
10548     },
10549      /**
10550      * Forces a resize - used by panel.Grid
10551      * @return {Element} The element
10552      */
10553     autoSize : function()
10554     {
10555         if(this.disableAutoSize) {
10556             return;
10557         }
10558         //var ctr = Roo.get(this.container.dom.parentElement);
10559         var ctr = Roo.get(this.el.dom);
10560         
10561         var thd = this.getGridEl().select('thead',true).first();
10562         var tbd = this.getGridEl().select('tbody', true).first();
10563         var tfd = this.getGridEl().select('tfoot', true).first();
10564         
10565         var cw = ctr.getWidth();
10566         this.getGridEl().select('tfoot tr, tfoot  td',true).setWidth(cw);
10567         
10568         if (tbd) {
10569             
10570             tbd.setWidth(ctr.getWidth());
10571             // if the body has a max height - and then scrolls - we should perhaps set up the height here
10572             // this needs fixing for various usage - currently only hydra job advers I think..
10573             //tdb.setHeight(
10574             //        ctr.getHeight() - ((thd ? thd.getHeight() : 0) + (tfd ? tfd.getHeight() : 0))
10575             //); 
10576             var barsize = (tbd.dom.offsetWidth - tbd.dom.clientWidth);
10577             cw -= barsize;
10578         }
10579         cw = Math.max(cw, this.totalWidth);
10580         this.getGridEl().select('tbody tr',true).setWidth(cw);
10581         this.initCSS();
10582         
10583         // resize 'expandable coloumn?
10584         
10585         return; // we doe not have a view in this design..
10586         
10587     },
10588     onBodyScroll: function()
10589     {
10590         //Roo.log("body scrolled');" + this.bodyEl.dom.scrollLeft);
10591         if(this.headEl){
10592             this.headEl.setStyle({
10593                 'position' : 'relative',
10594                 'left': (-1* this.bodyEl.dom.scrollLeft) + 'px'
10595             });
10596         }
10597         
10598         if(this.lazyLoad){
10599             
10600             var scrollHeight = this.bodyEl.dom.scrollHeight;
10601             
10602             var scrollTop = Math.ceil(this.bodyEl.getScroll().top);
10603             
10604             var height = this.bodyEl.getHeight();
10605             
10606             if(scrollHeight - height == scrollTop) {
10607                 
10608                 var total = this.ds.getTotalCount();
10609                 
10610                 if(this.footer.cursor + this.footer.pageSize < total){
10611                     
10612                     this.footer.ds.load({
10613                         params : {
10614                             start : this.footer.cursor + this.footer.pageSize,
10615                             limit : this.footer.pageSize
10616                         },
10617                         add : true
10618                     });
10619                 }
10620             }
10621             
10622         }
10623     },
10624     onColumnSplitterMoved : function(i, diff)
10625     {
10626         this.userResized = true;
10627         
10628         var cm = this.colModel;
10629         
10630         var w = this.getHeaderIndex(i).getWidth() + diff;
10631         
10632         
10633         cm.setColumnWidth(i, w, true);
10634         this.initCSS();
10635         //var cid = cm.getColumnId(i); << not used in this version?
10636        /* Roo.log(['#' + this.id + ' .x-col-' + i, "width", w + "px"]);
10637         
10638         this.CSS.updateRule( '#' + this.id + ' .x-col-' + i, "width", w + "px");
10639         this.CSS.updateRule('#' + this.id + ' .x-hcol-' + i, "width", w + "px");
10640         this.CSS.updateRule('#' + this.id + ' .x-grid-split-' + i, "left", w + "px");
10641 */
10642         //this.updateSplitters();
10643         //this.layout(); << ??
10644         this.fireEvent("columnresize", i, w);
10645     },
10646     onHeaderChange : function()
10647     {
10648         var header = this.renderHeader();
10649         var table = this.el.select('table', true).first();
10650         
10651         this.headEl.remove();
10652         this.headEl = table.createChild(header, this.bodyEl, false);
10653         
10654         Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
10655             e.on('click', this.sort, this);
10656         }, this);
10657         
10658         if(this.enableColumnResize !== false && Roo.grid.SplitDragZone){
10659             new Roo.grid.SplitDragZone(this, this.headEl.dom, false); // not sure what 'lockedHd is for this implementation..)
10660         }
10661         
10662     },
10663     
10664     onHiddenChange : function(colModel, colIndex, hidden)
10665     {
10666         /*
10667         this.cm.setHidden()
10668         var thSelector = '#' + this.id + ' .x-hcol-' + colIndex;
10669         var tdSelector = '#' + this.id + ' .x-col-' + colIndex;
10670         
10671         this.CSS.updateRule(thSelector, "display", "");
10672         this.CSS.updateRule(tdSelector, "display", "");
10673         
10674         if(hidden){
10675             this.CSS.updateRule(thSelector, "display", "none");
10676             this.CSS.updateRule(tdSelector, "display", "none");
10677         }
10678         */
10679         // onload calls initCSS()
10680         this.onHeaderChange();
10681         this.onLoad();
10682     },
10683     
10684     setColumnWidth: function(col_index, width)
10685     {
10686         // width = "md-2 xs-2..."
10687         if(!this.colModel.config[col_index]) {
10688             return;
10689         }
10690         
10691         var w = width.split(" ");
10692         
10693         var rows = this.el.dom.getElementsByClassName("x-col-"+col_index);
10694         
10695         var h_row = this.el.dom.getElementsByClassName("x-hcol-"+col_index);
10696         
10697         
10698         for(var j = 0; j < w.length; j++) {
10699             
10700             if(!w[j]) {
10701                 continue;
10702             }
10703             
10704             var size_cls = w[j].split("-");
10705             
10706             if(!Number.isInteger(size_cls[1] * 1)) {
10707                 continue;
10708             }
10709             
10710             if(!this.colModel.config[col_index][size_cls[0]]) {
10711                 continue;
10712             }
10713             
10714             if(!h_row[0].classList.contains("col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]])) {
10715                 continue;
10716             }
10717             
10718             h_row[0].classList.replace(
10719                 "col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]],
10720                 "col-"+size_cls[0]+"-"+size_cls[1]
10721             );
10722             
10723             for(var i = 0; i < rows.length; i++) {
10724                 
10725                 var size_cls = w[j].split("-");
10726                 
10727                 if(!Number.isInteger(size_cls[1] * 1)) {
10728                     continue;
10729                 }
10730                 
10731                 if(!this.colModel.config[col_index][size_cls[0]]) {
10732                     continue;
10733                 }
10734                 
10735                 if(!rows[i].classList.contains("col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]])) {
10736                     continue;
10737                 }
10738                 
10739                 rows[i].classList.replace(
10740                     "col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]],
10741                     "col-"+size_cls[0]+"-"+size_cls[1]
10742                 );
10743             }
10744             
10745             this.colModel.config[col_index][size_cls[0]] = size_cls[1];
10746         }
10747     }
10748 });
10749
10750 // currently only used to find the split on drag.. 
10751 Roo.bootstrap.Table.cellRE = /(?:.*?)x-grid-(?:hd|cell|split)-([\d]+)(?:.*?)/;
10752
10753 /**
10754  * @depricated
10755 */
10756 Roo.bootstrap.Table.AbstractSelectionModel = Roo.grid.AbstractSelectionModel;
10757 Roo.bootstrap.Table.RowSelectionModel = Roo.grid.RowSelectionModel;
10758 /*
10759  * - LGPL
10760  *
10761  * table cell
10762  * 
10763  */
10764
10765 /**
10766  * @class Roo.bootstrap.TableCell
10767  * @extends Roo.bootstrap.Component
10768  * @children Roo.bootstrap.Component
10769  * @parent Roo.bootstrap.TableRow
10770  * Bootstrap TableCell class
10771  * 
10772  * @cfg {String} html cell contain text
10773  * @cfg {String} cls cell class
10774  * @cfg {String} tag cell tag (td|th) default td
10775  * @cfg {String} abbr Specifies an abbreviated version of the content in a cell
10776  * @cfg {String} align Aligns the content in a cell
10777  * @cfg {String} axis Categorizes cells
10778  * @cfg {String} bgcolor Specifies the background color of a cell
10779  * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
10780  * @cfg {Number} colspan Specifies the number of columns a cell should span
10781  * @cfg {String} headers Specifies one or more header cells a cell is related to
10782  * @cfg {Number} height Sets the height of a cell
10783  * @cfg {String} nowrap Specifies that the content inside a cell should not wrap
10784  * @cfg {Number} rowspan Sets the number of rows a cell should span
10785  * @cfg {String} scope Defines a way to associate header cells and data cells in a table
10786  * @cfg {String} valign Vertical aligns the content in a cell
10787  * @cfg {Number} width Specifies the width of a cell
10788  * 
10789  * @constructor
10790  * Create a new TableCell
10791  * @param {Object} config The config object
10792  */
10793
10794 Roo.bootstrap.TableCell = function(config){
10795     Roo.bootstrap.TableCell.superclass.constructor.call(this, config);
10796 };
10797
10798 Roo.extend(Roo.bootstrap.TableCell, Roo.bootstrap.Component,  {
10799     
10800     html: false,
10801     cls: false,
10802     tag: false,
10803     abbr: false,
10804     align: false,
10805     axis: false,
10806     bgcolor: false,
10807     charoff: false,
10808     colspan: false,
10809     headers: false,
10810     height: false,
10811     nowrap: false,
10812     rowspan: false,
10813     scope: false,
10814     valign: false,
10815     width: false,
10816     
10817     
10818     getAutoCreate : function(){
10819         var cfg = Roo.apply({}, Roo.bootstrap.TableCell.superclass.getAutoCreate.call(this));
10820         
10821         cfg = {
10822             tag: 'td'
10823         };
10824         
10825         if(this.tag){
10826             cfg.tag = this.tag;
10827         }
10828         
10829         if (this.html) {
10830             cfg.html=this.html
10831         }
10832         if (this.cls) {
10833             cfg.cls=this.cls
10834         }
10835         if (this.abbr) {
10836             cfg.abbr=this.abbr
10837         }
10838         if (this.align) {
10839             cfg.align=this.align
10840         }
10841         if (this.axis) {
10842             cfg.axis=this.axis
10843         }
10844         if (this.bgcolor) {
10845             cfg.bgcolor=this.bgcolor
10846         }
10847         if (this.charoff) {
10848             cfg.charoff=this.charoff
10849         }
10850         if (this.colspan) {
10851             cfg.colspan=this.colspan
10852         }
10853         if (this.headers) {
10854             cfg.headers=this.headers
10855         }
10856         if (this.height) {
10857             cfg.height=this.height
10858         }
10859         if (this.nowrap) {
10860             cfg.nowrap=this.nowrap
10861         }
10862         if (this.rowspan) {
10863             cfg.rowspan=this.rowspan
10864         }
10865         if (this.scope) {
10866             cfg.scope=this.scope
10867         }
10868         if (this.valign) {
10869             cfg.valign=this.valign
10870         }
10871         if (this.width) {
10872             cfg.width=this.width
10873         }
10874         
10875         
10876         return cfg;
10877     }
10878    
10879 });
10880
10881  
10882
10883  /*
10884  * - LGPL
10885  *
10886  * table row
10887  * 
10888  */
10889
10890 /**
10891  * @class Roo.bootstrap.TableRow
10892  * @extends Roo.bootstrap.Component
10893  * @children Roo.bootstrap.TableCell
10894  * @parent Roo.bootstrap.TableBody
10895  * Bootstrap TableRow class
10896  * @cfg {String} cls row class
10897  * @cfg {String} align Aligns the content in a table row
10898  * @cfg {String} bgcolor Specifies a background color for a table row
10899  * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
10900  * @cfg {String} valign Vertical aligns the content in a table row
10901  * 
10902  * @constructor
10903  * Create a new TableRow
10904  * @param {Object} config The config object
10905  */
10906
10907 Roo.bootstrap.TableRow = function(config){
10908     Roo.bootstrap.TableRow.superclass.constructor.call(this, config);
10909 };
10910
10911 Roo.extend(Roo.bootstrap.TableRow, Roo.bootstrap.Component,  {
10912     
10913     cls: false,
10914     align: false,
10915     bgcolor: false,
10916     charoff: false,
10917     valign: false,
10918     
10919     getAutoCreate : function(){
10920         var cfg = Roo.apply({}, Roo.bootstrap.TableRow.superclass.getAutoCreate.call(this));
10921         
10922         cfg = {
10923             tag: 'tr'
10924         };
10925             
10926         if(this.cls){
10927             cfg.cls = this.cls;
10928         }
10929         if(this.align){
10930             cfg.align = this.align;
10931         }
10932         if(this.bgcolor){
10933             cfg.bgcolor = this.bgcolor;
10934         }
10935         if(this.charoff){
10936             cfg.charoff = this.charoff;
10937         }
10938         if(this.valign){
10939             cfg.valign = this.valign;
10940         }
10941         
10942         return cfg;
10943     }
10944    
10945 });
10946
10947  
10948
10949  /*
10950  * - LGPL
10951  *
10952  * table body
10953  * 
10954  */
10955
10956 /**
10957  * @class Roo.bootstrap.TableBody
10958  * @extends Roo.bootstrap.Component
10959  * @children Roo.bootstrap.TableRow
10960  * @parent Roo.bootstrap.Table
10961  * Bootstrap TableBody class
10962  * @cfg {String} cls element class
10963  * @cfg {String} tag element tag (thead|tbody|tfoot) default tbody
10964  * @cfg {String} align Aligns the content inside the element
10965  * @cfg {Number} charoff Sets the number of characters the content inside the element will be aligned from the character specified by the char attribute
10966  * @cfg {String} valign Vertical aligns the content inside the <tbody> element
10967  * 
10968  * @constructor
10969  * Create a new TableBody
10970  * @param {Object} config The config object
10971  */
10972
10973 Roo.bootstrap.TableBody = function(config){
10974     Roo.bootstrap.TableBody.superclass.constructor.call(this, config);
10975 };
10976
10977 Roo.extend(Roo.bootstrap.TableBody, Roo.bootstrap.Component,  {
10978     
10979     cls: false,
10980     tag: false,
10981     align: false,
10982     charoff: false,
10983     valign: false,
10984     
10985     getAutoCreate : function(){
10986         var cfg = Roo.apply({}, Roo.bootstrap.TableBody.superclass.getAutoCreate.call(this));
10987         
10988         cfg = {
10989             tag: 'tbody'
10990         };
10991             
10992         if (this.cls) {
10993             cfg.cls=this.cls
10994         }
10995         if(this.tag){
10996             cfg.tag = this.tag;
10997         }
10998         
10999         if(this.align){
11000             cfg.align = this.align;
11001         }
11002         if(this.charoff){
11003             cfg.charoff = this.charoff;
11004         }
11005         if(this.valign){
11006             cfg.valign = this.valign;
11007         }
11008         
11009         return cfg;
11010     }
11011     
11012     
11013 //    initEvents : function()
11014 //    {
11015 //        
11016 //        if(!this.store){
11017 //            return;
11018 //        }
11019 //        
11020 //        this.store = Roo.factory(this.store, Roo.data);
11021 //        this.store.on('load', this.onLoad, this);
11022 //        
11023 //        this.store.load();
11024 //        
11025 //    },
11026 //    
11027 //    onLoad: function () 
11028 //    {   
11029 //        this.fireEvent('load', this);
11030 //    }
11031 //    
11032 //   
11033 });
11034
11035  
11036
11037  /*
11038  * Based on:
11039  * Ext JS Library 1.1.1
11040  * Copyright(c) 2006-2007, Ext JS, LLC.
11041  *
11042  * Originally Released Under LGPL - original licence link has changed is not relivant.
11043  *
11044  * Fork - LGPL
11045  * <script type="text/javascript">
11046  */
11047
11048 // as we use this in bootstrap.
11049 Roo.namespace('Roo.form');
11050  /**
11051  * @class Roo.form.Action
11052  * Internal Class used to handle form actions
11053  * @constructor
11054  * @param {Roo.form.BasicForm} el The form element or its id
11055  * @param {Object} config Configuration options
11056  */
11057
11058  
11059  
11060 // define the action interface
11061 Roo.form.Action = function(form, options){
11062     this.form = form;
11063     this.options = options || {};
11064 };
11065 /**
11066  * Client Validation Failed
11067  * @const 
11068  */
11069 Roo.form.Action.CLIENT_INVALID = 'client';
11070 /**
11071  * Server Validation Failed
11072  * @const 
11073  */
11074 Roo.form.Action.SERVER_INVALID = 'server';
11075  /**
11076  * Connect to Server Failed
11077  * @const 
11078  */
11079 Roo.form.Action.CONNECT_FAILURE = 'connect';
11080 /**
11081  * Reading Data from Server Failed
11082  * @const 
11083  */
11084 Roo.form.Action.LOAD_FAILURE = 'load';
11085
11086 Roo.form.Action.prototype = {
11087     type : 'default',
11088     failureType : undefined,
11089     response : undefined,
11090     result : undefined,
11091
11092     // interface method
11093     run : function(options){
11094
11095     },
11096
11097     // interface method
11098     success : function(response){
11099
11100     },
11101
11102     // interface method
11103     handleResponse : function(response){
11104
11105     },
11106
11107     // default connection failure
11108     failure : function(response){
11109         
11110         this.response = response;
11111         this.failureType = Roo.form.Action.CONNECT_FAILURE;
11112         this.form.afterAction(this, false);
11113     },
11114
11115     processResponse : function(response){
11116         this.response = response;
11117         if(!response.responseText){
11118             return true;
11119         }
11120         this.result = this.handleResponse(response);
11121         return this.result;
11122     },
11123
11124     // utility functions used internally
11125     getUrl : function(appendParams){
11126         var url = this.options.url || this.form.url || this.form.el.dom.action;
11127         if(appendParams){
11128             var p = this.getParams();
11129             if(p){
11130                 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
11131             }
11132         }
11133         return url;
11134     },
11135
11136     getMethod : function(){
11137         return (this.options.method || this.form.method || this.form.el.dom.method || 'POST').toUpperCase();
11138     },
11139
11140     getParams : function(){
11141         var bp = this.form.baseParams;
11142         var p = this.options.params;
11143         if(p){
11144             if(typeof p == "object"){
11145                 p = Roo.urlEncode(Roo.applyIf(p, bp));
11146             }else if(typeof p == 'string' && bp){
11147                 p += '&' + Roo.urlEncode(bp);
11148             }
11149         }else if(bp){
11150             p = Roo.urlEncode(bp);
11151         }
11152         return p;
11153     },
11154
11155     createCallback : function(){
11156         return {
11157             success: this.success,
11158             failure: this.failure,
11159             scope: this,
11160             timeout: (this.form.timeout*1000),
11161             upload: this.form.fileUpload ? this.success : undefined
11162         };
11163     }
11164 };
11165
11166 Roo.form.Action.Submit = function(form, options){
11167     Roo.form.Action.Submit.superclass.constructor.call(this, form, options);
11168 };
11169
11170 Roo.extend(Roo.form.Action.Submit, Roo.form.Action, {
11171     type : 'submit',
11172
11173     haveProgress : false,
11174     uploadComplete : false,
11175     
11176     // uploadProgress indicator.
11177     uploadProgress : function()
11178     {
11179         if (!this.form.progressUrl) {
11180             return;
11181         }
11182         
11183         if (!this.haveProgress) {
11184             Roo.MessageBox.progress("Uploading", "Uploading");
11185         }
11186         if (this.uploadComplete) {
11187            Roo.MessageBox.hide();
11188            return;
11189         }
11190         
11191         this.haveProgress = true;
11192    
11193         var uid = this.form.findField('UPLOAD_IDENTIFIER').getValue();
11194         
11195         var c = new Roo.data.Connection();
11196         c.request({
11197             url : this.form.progressUrl,
11198             params: {
11199                 id : uid
11200             },
11201             method: 'GET',
11202             success : function(req){
11203                //console.log(data);
11204                 var rdata = false;
11205                 var edata;
11206                 try  {
11207                    rdata = Roo.decode(req.responseText)
11208                 } catch (e) {
11209                     Roo.log("Invalid data from server..");
11210                     Roo.log(edata);
11211                     return;
11212                 }
11213                 if (!rdata || !rdata.success) {
11214                     Roo.log(rdata);
11215                     Roo.MessageBox.alert(Roo.encode(rdata));
11216                     return;
11217                 }
11218                 var data = rdata.data;
11219                 
11220                 if (this.uploadComplete) {
11221                    Roo.MessageBox.hide();
11222                    return;
11223                 }
11224                    
11225                 if (data){
11226                     Roo.MessageBox.updateProgress(data.bytes_uploaded/data.bytes_total,
11227                        Math.floor((data.bytes_total - data.bytes_uploaded)/1000) + 'k remaining'
11228                     );
11229                 }
11230                 this.uploadProgress.defer(2000,this);
11231             },
11232        
11233             failure: function(data) {
11234                 Roo.log('progress url failed ');
11235                 Roo.log(data);
11236             },
11237             scope : this
11238         });
11239            
11240     },
11241     
11242     
11243     run : function()
11244     {
11245         // run get Values on the form, so it syncs any secondary forms.
11246         this.form.getValues();
11247         
11248         var o = this.options;
11249         var method = this.getMethod();
11250         var isPost = method == 'POST';
11251         if(o.clientValidation === false || this.form.isValid()){
11252             
11253             if (this.form.progressUrl) {
11254                 this.form.findField('UPLOAD_IDENTIFIER').setValue(
11255                     (new Date() * 1) + '' + Math.random());
11256                     
11257             } 
11258             
11259             
11260             Roo.Ajax.request(Roo.apply(this.createCallback(), {
11261                 form:this.form.el.dom,
11262                 url:this.getUrl(!isPost),
11263                 method: method,
11264                 params:isPost ? this.getParams() : null,
11265                 isUpload: this.form.fileUpload,
11266                 formData : this.form.formData
11267             }));
11268             
11269             this.uploadProgress();
11270
11271         }else if (o.clientValidation !== false){ // client validation failed
11272             this.failureType = Roo.form.Action.CLIENT_INVALID;
11273             this.form.afterAction(this, false);
11274         }
11275     },
11276
11277     success : function(response)
11278     {
11279         this.uploadComplete= true;
11280         if (this.haveProgress) {
11281             Roo.MessageBox.hide();
11282         }
11283         
11284         
11285         var result = this.processResponse(response);
11286         if(result === true || result.success){
11287             this.form.afterAction(this, true);
11288             return;
11289         }
11290         if(result.errors){
11291             this.form.markInvalid(result.errors);
11292             this.failureType = Roo.form.Action.SERVER_INVALID;
11293         }
11294         this.form.afterAction(this, false);
11295     },
11296     failure : function(response)
11297     {
11298         this.uploadComplete= true;
11299         if (this.haveProgress) {
11300             Roo.MessageBox.hide();
11301         }
11302         
11303         this.response = response;
11304         this.failureType = Roo.form.Action.CONNECT_FAILURE;
11305         this.form.afterAction(this, false);
11306     },
11307     
11308     handleResponse : function(response){
11309         if(this.form.errorReader){
11310             var rs = this.form.errorReader.read(response);
11311             var errors = [];
11312             if(rs.records){
11313                 for(var i = 0, len = rs.records.length; i < len; i++) {
11314                     var r = rs.records[i];
11315                     errors[i] = r.data;
11316                 }
11317             }
11318             if(errors.length < 1){
11319                 errors = null;
11320             }
11321             return {
11322                 success : rs.success,
11323                 errors : errors
11324             };
11325         }
11326         var ret = false;
11327         try {
11328             ret = Roo.decode(response.responseText);
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         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
12551         
12552         var id = Roo.id();
12553         
12554         var cfg = {};
12555         
12556         if(this.inputType != 'hidden'){
12557             cfg.cls = 'form-group' //input-group
12558         }
12559         
12560         var input =  {
12561             tag: 'input',
12562             id : id,
12563             type : this.inputType,
12564             value : this.value,
12565             cls : 'form-control',
12566             placeholder : this.placeholder || '',
12567             autocomplete : this.autocomplete || 'new-password'
12568         };
12569         if (this.inputType == 'file') {
12570             input.style = 'overflow:hidden'; // why not in CSS?
12571         }
12572         
12573         if(this.capture.length){
12574             input.capture = this.capture;
12575         }
12576         
12577         if(this.accept.length){
12578             input.accept = this.accept + "/*";
12579         }
12580         
12581         if(this.align){
12582             input.style = (typeof(input.style) == 'undefined') ? ('text-align:' + this.align) : (input.style + 'text-align:' + this.align);
12583         }
12584         
12585         if(this.maxLength && this.maxLength != Number.MAX_VALUE){
12586             input.maxLength = this.maxLength;
12587         }
12588         
12589         if (this.disabled) {
12590             input.disabled=true;
12591         }
12592         
12593         if (this.readOnly) {
12594             input.readonly=true;
12595         }
12596         
12597         if (this.name) {
12598             input.name = this.name;
12599         }
12600         
12601         if (this.size) {
12602             input.cls += ' input-' + this.size;
12603         }
12604         
12605         var settings=this;
12606         ['xs','sm','md','lg'].map(function(size){
12607             if (settings[size]) {
12608                 cfg.cls += ' col-' + size + '-' + settings[size];
12609             }
12610         });
12611         
12612         var inputblock = input;
12613         
12614         var feedback = {
12615             tag: 'span',
12616             cls: 'glyphicon form-control-feedback'
12617         };
12618             
12619         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
12620             
12621             inputblock = {
12622                 cls : 'has-feedback',
12623                 cn :  [
12624                     input,
12625                     feedback
12626                 ] 
12627             };  
12628         }
12629         
12630         if (this.before || this.after) {
12631             
12632             inputblock = {
12633                 cls : 'input-group',
12634                 cn :  [] 
12635             };
12636             
12637             if (this.before && typeof(this.before) == 'string') {
12638                 
12639                 inputblock.cn.push({
12640                     tag :'span',
12641                     cls : 'roo-input-before input-group-addon input-group-prepend input-group-text',
12642                     html : this.before
12643                 });
12644             }
12645             if (this.before && typeof(this.before) == 'object') {
12646                 this.before = Roo.factory(this.before);
12647                 
12648                 inputblock.cn.push({
12649                     tag :'span',
12650                     cls : 'roo-input-before input-group-prepend   input-group-' +
12651                         (this.before.xtype == 'Button' ? 'btn' : 'addon')  //?? what about checkboxes - that looks like a bit of a hack thought? 
12652                 });
12653             }
12654             
12655             inputblock.cn.push(input);
12656             
12657             if (this.after && typeof(this.after) == 'string') {
12658                 inputblock.cn.push({
12659                     tag :'span',
12660                     cls : 'roo-input-after input-group-append input-group-text input-group-addon',
12661                     html : this.after
12662                 });
12663             }
12664             if (this.after && typeof(this.after) == 'object') {
12665                 this.after = Roo.factory(this.after);
12666                 
12667                 inputblock.cn.push({
12668                     tag :'span',
12669                     cls : 'roo-input-after input-group-append  input-group-' +
12670                         (this.after.xtype == 'Button' ? 'btn' : 'addon')  //?? what about checkboxes - that looks like a bit of a hack thought? 
12671                 });
12672             }
12673             
12674             if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
12675                 inputblock.cls += ' has-feedback';
12676                 inputblock.cn.push(feedback);
12677             }
12678         };
12679         var indicator = {
12680             tag : 'i',
12681             cls : 'roo-required-indicator ' + (this.indicatorpos == 'right'  ? 'right' : 'left') +'-indicator text-danger fa fa-lg fa-star',
12682             tooltip : 'This field is required'
12683         };
12684         if (this.allowBlank ) {
12685             indicator.style = this.allowBlank ? ' display:none' : '';
12686         }
12687         if (align ==='left' && this.fieldLabel.length) {
12688             
12689             cfg.cls += ' roo-form-group-label-left'  + (Roo.bootstrap.version == 4 ? ' row' : '');
12690             
12691             cfg.cn = [
12692                 indicator,
12693                 {
12694                     tag: 'label',
12695                     'for' :  id,
12696                     cls : 'control-label col-form-label',
12697                     html : this.fieldLabel
12698
12699                 },
12700                 {
12701                     cls : "", 
12702                     cn: [
12703                         inputblock
12704                     ]
12705                 }
12706             ];
12707             
12708             var labelCfg = cfg.cn[1];
12709             var contentCfg = cfg.cn[2];
12710             
12711             if(this.indicatorpos == 'right'){
12712                 cfg.cn = [
12713                     {
12714                         tag: 'label',
12715                         'for' :  id,
12716                         cls : 'control-label col-form-label',
12717                         cn : [
12718                             {
12719                                 tag : 'span',
12720                                 html : this.fieldLabel
12721                             },
12722                             indicator
12723                         ]
12724                     },
12725                     {
12726                         cls : "",
12727                         cn: [
12728                             inputblock
12729                         ]
12730                     }
12731
12732                 ];
12733                 
12734                 labelCfg = cfg.cn[0];
12735                 contentCfg = cfg.cn[1];
12736             
12737             }
12738             
12739             if(this.labelWidth > 12){
12740                 labelCfg.style = "width: " + this.labelWidth + 'px';
12741             }
12742             
12743             if(this.labelWidth < 13 && this.labelmd == 0){
12744                 this.labellg = this.labellg > 0 ? this.labellg : this.labelWidth;
12745             }
12746             
12747             if(this.labellg > 0){
12748                 labelCfg.cls += ' col-lg-' + this.labellg;
12749                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
12750             }
12751             
12752             if(this.labelmd > 0){
12753                 labelCfg.cls += ' col-md-' + this.labelmd;
12754                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
12755             }
12756             
12757             if(this.labelsm > 0){
12758                 labelCfg.cls += ' col-sm-' + this.labelsm;
12759                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
12760             }
12761             
12762             if(this.labelxs > 0){
12763                 labelCfg.cls += ' col-xs-' + this.labelxs;
12764                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
12765             }
12766             
12767             
12768         } else if ( this.fieldLabel.length) {
12769                 
12770             
12771             
12772             cfg.cn = [
12773                 {
12774                     tag : 'i',
12775                     cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
12776                     tooltip : 'This field is required',
12777                     style : this.allowBlank ? ' display:none' : '' 
12778                 },
12779                 {
12780                     tag: 'label',
12781                    //cls : 'input-group-addon',
12782                     html : this.fieldLabel
12783
12784                 },
12785
12786                inputblock
12787
12788            ];
12789            
12790            if(this.indicatorpos == 'right'){
12791        
12792                 cfg.cn = [
12793                     {
12794                         tag: 'label',
12795                        //cls : 'input-group-addon',
12796                         html : this.fieldLabel
12797
12798                     },
12799                     {
12800                         tag : 'i',
12801                         cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
12802                         tooltip : 'This field is required',
12803                         style : this.allowBlank ? ' display:none' : '' 
12804                     },
12805
12806                    inputblock
12807
12808                ];
12809
12810             }
12811
12812         } else {
12813             
12814             cfg.cn = [
12815
12816                     inputblock
12817
12818             ];
12819                 
12820                 
12821         };
12822         
12823         if (this.parentType === 'Navbar' &&  this.parent().bar) {
12824            cfg.cls += ' navbar-form';
12825         }
12826         
12827         if (this.parentType === 'NavGroup' && !(Roo.bootstrap.version == 4 && this.parent().form)) {
12828             // on BS4 we do this only if not form 
12829             cfg.cls += ' navbar-form';
12830             cfg.tag = 'li';
12831         }
12832         
12833         return cfg;
12834         
12835     },
12836     /**
12837      * return the real input element.
12838      */
12839     inputEl: function ()
12840     {
12841         return this.el.select('input.form-control',true).first();
12842     },
12843     
12844     tooltipEl : function()
12845     {
12846         return this.inputEl();
12847     },
12848     
12849     indicatorEl : function()
12850     {
12851         if (Roo.bootstrap.version == 4) {
12852             return false; // not enabled in v4 yet.
12853         }
12854         
12855         var indicator = this.el.select('i.roo-required-indicator',true).first();
12856         
12857         if(!indicator){
12858             return false;
12859         }
12860         
12861         return indicator;
12862         
12863     },
12864     
12865     setDisabled : function(v)
12866     {
12867         var i  = this.inputEl().dom;
12868         if (!v) {
12869             i.removeAttribute('disabled');
12870             return;
12871             
12872         }
12873         i.setAttribute('disabled','true');
12874     },
12875     initEvents : function()
12876     {
12877           
12878         this.inputEl().on("keydown" , this.fireKey,  this);
12879         this.inputEl().on("focus", this.onFocus,  this);
12880         this.inputEl().on("blur", this.onBlur,  this);
12881         
12882         this.inputEl().relayEvent('keyup', this);
12883         this.inputEl().relayEvent('paste', this);
12884         
12885         this.indicator = this.indicatorEl();
12886         
12887         if(this.indicator){
12888             this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible'); // changed from invisible??? - 
12889         }
12890  
12891         // reference to original value for reset
12892         this.originalValue = this.getValue();
12893         //Roo.form.TextField.superclass.initEvents.call(this);
12894         if(this.validationEvent == 'keyup'){
12895             this.validationTask = new Roo.util.DelayedTask(this.validate, this);
12896             this.inputEl().on('keyup', this.filterValidation, this);
12897         }
12898         else if(this.validationEvent !== false){
12899             this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
12900         }
12901         
12902         if(this.selectOnFocus){
12903             this.on("focus", this.preFocus, this);
12904             
12905         }
12906         if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
12907             this.inputEl().on("keypress", this.filterKeys, this);
12908         } else {
12909             this.inputEl().relayEvent('keypress', this);
12910         }
12911        /* if(this.grow){
12912             this.el.on("keyup", this.onKeyUp,  this, {buffer:50});
12913             this.el.on("click", this.autoSize,  this);
12914         }
12915         */
12916         if(this.inputEl().is('input[type=password]') && Roo.isSafari){
12917             this.inputEl().on('keydown', this.SafariOnKeyDown, this);
12918         }
12919         
12920         if (typeof(this.before) == 'object') {
12921             this.before.render(this.el.select('.roo-input-before',true).first());
12922         }
12923         if (typeof(this.after) == 'object') {
12924             this.after.render(this.el.select('.roo-input-after',true).first());
12925         }
12926         
12927         this.inputEl().on('change', this.onChange, this);
12928         
12929     },
12930     filterValidation : function(e){
12931         if(!e.isNavKeyPress()){
12932             this.validationTask.delay(this.validationDelay);
12933         }
12934     },
12935      /**
12936      * Validates the field value
12937      * @return {Boolean} True if the value is valid, else false
12938      */
12939     validate : function(){
12940         //if(this.disabled || this.validateValue(this.processValue(this.getRawValue()))){
12941         if(this.disabled || this.validateValue(this.getRawValue())){
12942             this.markValid();
12943             return true;
12944         }
12945         
12946         this.markInvalid();
12947         return false;
12948     },
12949     
12950     
12951     /**
12952      * Validates a value according to the field's validation rules and marks the field as invalid
12953      * if the validation fails
12954      * @param {Mixed} value The value to validate
12955      * @return {Boolean} True if the value is valid, else false
12956      */
12957     validateValue : function(value)
12958     {
12959         if(this.getVisibilityEl().hasClass('hidden')){
12960             return true;
12961         }
12962         
12963         if(value.length < 1)  { // if it's blank
12964             if(this.allowBlank){
12965                 return true;
12966             }
12967             return false;
12968         }
12969         
12970         if(value.length < this.minLength){
12971             return false;
12972         }
12973         if(value.length > this.maxLength){
12974             return false;
12975         }
12976         if(this.vtype){
12977             var vt = Roo.form.VTypes;
12978             if(!vt[this.vtype](value, this)){
12979                 return false;
12980             }
12981         }
12982         if(typeof this.validator == "function"){
12983             var msg = this.validator(value);
12984             if (typeof(msg) == 'string') {
12985                 this.invalidText = msg;
12986             }
12987             if(msg !== true){
12988                 return false;
12989             }
12990         }
12991         
12992         if(this.regex && !this.regex.test(value)){
12993             return false;
12994         }
12995         
12996         return true;
12997     },
12998     
12999      // private
13000     fireKey : function(e){
13001         //Roo.log('field ' + e.getKey());
13002         if(e.isNavKeyPress()){
13003             this.fireEvent("specialkey", this, e);
13004         }
13005     },
13006     focus : function (selectText){
13007         if(this.rendered){
13008             this.inputEl().focus();
13009             if(selectText === true){
13010                 this.inputEl().dom.select();
13011             }
13012         }
13013         return this;
13014     } ,
13015     
13016     onFocus : function(){
13017         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
13018            // this.el.addClass(this.focusClass);
13019         }
13020         if(!this.hasFocus){
13021             this.hasFocus = true;
13022             this.startValue = this.getValue();
13023             this.fireEvent("focus", this);
13024         }
13025     },
13026     
13027     beforeBlur : Roo.emptyFn,
13028
13029     
13030     // private
13031     onBlur : function(){
13032         this.beforeBlur();
13033         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
13034             //this.el.removeClass(this.focusClass);
13035         }
13036         this.hasFocus = false;
13037         if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
13038             this.validate();
13039         }
13040         var v = this.getValue();
13041         if(String(v) !== String(this.startValue)){
13042             this.fireEvent('change', this, v, this.startValue);
13043         }
13044         this.fireEvent("blur", this);
13045     },
13046     
13047     onChange : function(e)
13048     {
13049         var v = this.getValue();
13050         if(String(v) !== String(this.startValue)){
13051             this.fireEvent('change', this, v, this.startValue);
13052         }
13053         
13054     },
13055     
13056     /**
13057      * Resets the current field value to the originally loaded value and clears any validation messages
13058      */
13059     reset : function(){
13060         this.setValue(this.originalValue);
13061         this.validate();
13062     },
13063      /**
13064      * Returns the name of the field
13065      * @return {Mixed} name The name field
13066      */
13067     getName: function(){
13068         return this.name;
13069     },
13070      /**
13071      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
13072      * @return {Mixed} value The field value
13073      */
13074     getValue : function(){
13075         
13076         var v = this.inputEl().getValue();
13077         
13078         return v;
13079     },
13080     /**
13081      * Returns the raw data value which may or may not be a valid, defined value.  To return a normalized value see {@link #getValue}.
13082      * @return {Mixed} value The field value
13083      */
13084     getRawValue : function(){
13085         var v = this.inputEl().getValue();
13086         
13087         return v;
13088     },
13089     
13090     /**
13091      * Sets the underlying DOM field's value directly, bypassing validation.  To set the value with validation see {@link #setValue}.
13092      * @param {Mixed} value The value to set
13093      */
13094     setRawValue : function(v){
13095         return this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
13096     },
13097     
13098     selectText : function(start, end){
13099         var v = this.getRawValue();
13100         if(v.length > 0){
13101             start = start === undefined ? 0 : start;
13102             end = end === undefined ? v.length : end;
13103             var d = this.inputEl().dom;
13104             if(d.setSelectionRange){
13105                 d.setSelectionRange(start, end);
13106             }else if(d.createTextRange){
13107                 var range = d.createTextRange();
13108                 range.moveStart("character", start);
13109                 range.moveEnd("character", v.length-end);
13110                 range.select();
13111             }
13112         }
13113     },
13114     
13115     /**
13116      * Sets a data value into the field and validates it.  To set the value directly without validation see {@link #setRawValue}.
13117      * @param {Mixed} value The value to set
13118      */
13119     setValue : function(v){
13120         this.value = v;
13121         if(this.rendered){
13122             this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
13123             this.validate();
13124         }
13125     },
13126     
13127     /*
13128     processValue : function(value){
13129         if(this.stripCharsRe){
13130             var newValue = value.replace(this.stripCharsRe, '');
13131             if(newValue !== value){
13132                 this.setRawValue(newValue);
13133                 return newValue;
13134             }
13135         }
13136         return value;
13137     },
13138   */
13139     preFocus : function(){
13140         
13141         if(this.selectOnFocus){
13142             this.inputEl().dom.select();
13143         }
13144     },
13145     filterKeys : function(e){
13146         var k = e.getKey();
13147         if(!Roo.isIE && (e.isNavKeyPress() || k == e.BACKSPACE || (k == e.DELETE && e.button == -1))){
13148             return;
13149         }
13150         var c = e.getCharCode(), cc = String.fromCharCode(c);
13151         if(Roo.isIE && (e.isSpecialKey() || !cc)){
13152             return;
13153         }
13154         if(!this.maskRe.test(cc)){
13155             e.stopEvent();
13156         }
13157     },
13158      /**
13159      * Clear any invalid styles/messages for this field
13160      */
13161     clearInvalid : function(){
13162         
13163         if(!this.el || this.preventMark){ // not rendered
13164             return;
13165         }
13166         
13167         
13168         this.el.removeClass([this.invalidClass, 'is-invalid']);
13169         
13170         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
13171             
13172             var feedback = this.el.select('.form-control-feedback', true).first();
13173             
13174             if(feedback){
13175                 this.el.select('.form-control-feedback', true).first().removeClass(this.invalidFeedbackClass);
13176             }
13177             
13178         }
13179         
13180         if(this.indicator){
13181             this.indicator.removeClass('visible');
13182             this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
13183         }
13184         
13185         this.fireEvent('valid', this);
13186     },
13187     
13188      /**
13189      * Mark this field as valid
13190      */
13191     markValid : function()
13192     {
13193         if(!this.el  || this.preventMark){ // not rendered...
13194             return;
13195         }
13196         
13197         this.el.removeClass([this.invalidClass, this.validClass]);
13198         this.inputEl().removeClass(['is-valid', 'is-invalid']);
13199
13200         var feedback = this.el.select('.form-control-feedback', true).first();
13201             
13202         if(feedback){
13203             this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
13204         }
13205         
13206         if(this.indicator){
13207             this.indicator.removeClass('visible');
13208             this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
13209         }
13210         
13211         if(this.disabled){
13212             return;
13213         }
13214         
13215            
13216         if(this.allowBlank && !this.getRawValue().length){
13217             return;
13218         }
13219         if (Roo.bootstrap.version == 3) {
13220             this.el.addClass(this.validClass);
13221         } else {
13222             this.inputEl().addClass('is-valid');
13223         }
13224
13225         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank && (this.getValue().length || this.forceFeedback)){
13226             
13227             var feedback = this.el.select('.form-control-feedback', true).first();
13228             
13229             if(feedback){
13230                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
13231                 this.el.select('.form-control-feedback', true).first().addClass([this.validFeedbackClass]);
13232             }
13233             
13234         }
13235         
13236         this.fireEvent('valid', this);
13237     },
13238     
13239      /**
13240      * Mark this field as invalid
13241      * @param {String} msg The validation message
13242      */
13243     markInvalid : function(msg)
13244     {
13245         if(!this.el  || this.preventMark){ // not rendered
13246             return;
13247         }
13248         
13249         this.el.removeClass([this.invalidClass, this.validClass]);
13250         this.inputEl().removeClass(['is-valid', 'is-invalid']);
13251         
13252         var feedback = this.el.select('.form-control-feedback', true).first();
13253             
13254         if(feedback){
13255             this.el.select('.form-control-feedback', true).first().removeClass(
13256                     [this.invalidFeedbackClass, this.validFeedbackClass]);
13257         }
13258
13259         if(this.disabled){
13260             return;
13261         }
13262         
13263         if(this.allowBlank && !this.getRawValue().length){
13264             return;
13265         }
13266         
13267         if(this.indicator){
13268             this.indicator.removeClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
13269             this.indicator.addClass('visible');
13270         }
13271         if (Roo.bootstrap.version == 3) {
13272             this.el.addClass(this.invalidClass);
13273         } else {
13274             this.inputEl().addClass('is-invalid');
13275         }
13276         
13277         
13278         
13279         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
13280             
13281             var feedback = this.el.select('.form-control-feedback', true).first();
13282             
13283             if(feedback){
13284                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
13285                 
13286                 if(this.getValue().length || this.forceFeedback){
13287                     this.el.select('.form-control-feedback', true).first().addClass([this.invalidFeedbackClass]);
13288                 }
13289                 
13290             }
13291             
13292         }
13293         
13294         this.fireEvent('invalid', this, msg);
13295     },
13296     // private
13297     SafariOnKeyDown : function(event)
13298     {
13299         // this is a workaround for a password hang bug on chrome/ webkit.
13300         if (this.inputEl().dom.type != 'password') {
13301             return;
13302         }
13303         
13304         var isSelectAll = false;
13305         
13306         if(this.inputEl().dom.selectionEnd > 0){
13307             isSelectAll = (this.inputEl().dom.selectionEnd - this.inputEl().dom.selectionStart - this.getValue().length == 0) ? true : false;
13308         }
13309         if(((event.getKey() == 8 || event.getKey() == 46) && this.getValue().length ==1)){ // backspace and delete key
13310             event.preventDefault();
13311             this.setValue('');
13312             return;
13313         }
13314         
13315         if(isSelectAll  && event.getCharCode() > 31 && !event.ctrlKey) { // not backspace and delete key (or ctrl-v)
13316             
13317             event.preventDefault();
13318             // this is very hacky as keydown always get's upper case.
13319             //
13320             var cc = String.fromCharCode(event.getCharCode());
13321             this.setValue( event.shiftKey ?  cc : cc.toLowerCase());
13322             
13323         }
13324     },
13325     adjustWidth : function(tag, w){
13326         tag = tag.toLowerCase();
13327         if(typeof w == 'number' && Roo.isStrict && !Roo.isSafari){
13328             if(Roo.isIE && (tag == 'input' || tag == 'textarea')){
13329                 if(tag == 'input'){
13330                     return w + 2;
13331                 }
13332                 if(tag == 'textarea'){
13333                     return w-2;
13334                 }
13335             }else if(Roo.isOpera){
13336                 if(tag == 'input'){
13337                     return w + 2;
13338                 }
13339                 if(tag == 'textarea'){
13340                     return w-2;
13341                 }
13342             }
13343         }
13344         return w;
13345     },
13346     
13347     setFieldLabel : function(v)
13348     {
13349         if(!this.rendered){
13350             return;
13351         }
13352         
13353         if(this.indicatorEl()){
13354             var ar = this.el.select('label > span',true);
13355             
13356             if (ar.elements.length) {
13357                 this.el.select('label > span',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
13358                 this.fieldLabel = v;
13359                 return;
13360             }
13361             
13362             var br = this.el.select('label',true);
13363             
13364             if(br.elements.length) {
13365                 this.el.select('label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
13366                 this.fieldLabel = v;
13367                 return;
13368             }
13369             
13370             Roo.log('Cannot Found any of label > span || label in input');
13371             return;
13372         }
13373         
13374         this.el.select('label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
13375         this.fieldLabel = v;
13376         
13377         
13378     }
13379 });
13380
13381  
13382 /*
13383  * - LGPL
13384  *
13385  * Input
13386  * 
13387  */
13388
13389 /**
13390  * @class Roo.bootstrap.form.TextArea
13391  * @extends Roo.bootstrap.form.Input
13392  * Bootstrap TextArea class
13393  * @cfg {Number} cols Specifies the visible width of a text area
13394  * @cfg {Number} rows Specifies the visible number of lines in a text area
13395  * @cfg {string} wrap (soft|hard)Specifies how the text in a text area is to be wrapped when submitted in a form
13396  * @cfg {string} resize (none|both|horizontal|vertical|inherit|initial)
13397  * @cfg {string} html text
13398  * 
13399  * @constructor
13400  * Create a new TextArea
13401  * @param {Object} config The config object
13402  */
13403
13404 Roo.bootstrap.form.TextArea = function(config){
13405     Roo.bootstrap.form.TextArea.superclass.constructor.call(this, config);
13406    
13407 };
13408
13409 Roo.extend(Roo.bootstrap.form.TextArea, Roo.bootstrap.form.Input,  {
13410      
13411     cols : false,
13412     rows : 5,
13413     readOnly : false,
13414     warp : 'soft',
13415     resize : false,
13416     value: false,
13417     html: false,
13418     
13419     getAutoCreate : function(){
13420         
13421         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
13422         
13423         var id = Roo.id();
13424         
13425         var cfg = {};
13426         
13427         if(this.inputType != 'hidden'){
13428             cfg.cls = 'form-group' //input-group
13429         }
13430         
13431         var input =  {
13432             tag: 'textarea',
13433             id : id,
13434             warp : this.warp,
13435             rows : this.rows,
13436             value : this.value || '',
13437             html: this.html || '',
13438             cls : 'form-control',
13439             placeholder : this.placeholder || '' 
13440             
13441         };
13442         
13443         if(this.maxLength && this.maxLength != Number.MAX_VALUE){
13444             input.maxLength = this.maxLength;
13445         }
13446         
13447         if(this.resize){
13448             input.style = (typeof(input.style) == 'undefined') ? 'resize:' + this.resize : input.style + 'resize:' + this.resize;
13449         }
13450         
13451         if(this.cols){
13452             input.cols = this.cols;
13453         }
13454         
13455         if (this.readOnly) {
13456             input.readonly = true;
13457         }
13458         
13459         if (this.name) {
13460             input.name = this.name;
13461         }
13462         
13463         if (this.size) {
13464             input.cls = (typeof(input.cls) == 'undefined') ? 'input-' + this.size : input.cls + ' input-' + this.size;
13465         }
13466         
13467         var settings=this;
13468         ['xs','sm','md','lg'].map(function(size){
13469             if (settings[size]) {
13470                 cfg.cls += ' col-' + size + '-' + settings[size];
13471             }
13472         });
13473         
13474         var inputblock = input;
13475         
13476         if(this.hasFeedback && !this.allowBlank){
13477             
13478             var feedback = {
13479                 tag: 'span',
13480                 cls: 'glyphicon form-control-feedback'
13481             };
13482
13483             inputblock = {
13484                 cls : 'has-feedback',
13485                 cn :  [
13486                     input,
13487                     feedback
13488                 ] 
13489             };  
13490         }
13491         
13492         
13493         if (this.before || this.after) {
13494             
13495             inputblock = {
13496                 cls : 'input-group',
13497                 cn :  [] 
13498             };
13499             if (this.before) {
13500                 inputblock.cn.push({
13501                     tag :'span',
13502                     cls : 'input-group-addon',
13503                     html : this.before
13504                 });
13505             }
13506             
13507             inputblock.cn.push(input);
13508             
13509             if(this.hasFeedback && !this.allowBlank){
13510                 inputblock.cls += ' has-feedback';
13511                 inputblock.cn.push(feedback);
13512             }
13513             
13514             if (this.after) {
13515                 inputblock.cn.push({
13516                     tag :'span',
13517                     cls : 'input-group-addon',
13518                     html : this.after
13519                 });
13520             }
13521             
13522         }
13523         
13524         if (align ==='left' && this.fieldLabel.length) {
13525             cfg.cn = [
13526                 {
13527                     tag: 'label',
13528                     'for' :  id,
13529                     cls : 'control-label',
13530                     html : this.fieldLabel
13531                 },
13532                 {
13533                     cls : "",
13534                     cn: [
13535                         inputblock
13536                     ]
13537                 }
13538
13539             ];
13540             
13541             if(this.labelWidth > 12){
13542                 cfg.cn[0].style = "width: " + this.labelWidth + 'px';
13543             }
13544
13545             if(this.labelWidth < 13 && this.labelmd == 0){
13546                 this.labelmd = this.labelWidth;
13547             }
13548
13549             if(this.labellg > 0){
13550                 cfg.cn[0].cls += ' col-lg-' + this.labellg;
13551                 cfg.cn[1].cls += ' col-lg-' + (12 - this.labellg);
13552             }
13553
13554             if(this.labelmd > 0){
13555                 cfg.cn[0].cls += ' col-md-' + this.labelmd;
13556                 cfg.cn[1].cls += ' col-md-' + (12 - this.labelmd);
13557             }
13558
13559             if(this.labelsm > 0){
13560                 cfg.cn[0].cls += ' col-sm-' + this.labelsm;
13561                 cfg.cn[1].cls += ' col-sm-' + (12 - this.labelsm);
13562             }
13563
13564             if(this.labelxs > 0){
13565                 cfg.cn[0].cls += ' col-xs-' + this.labelxs;
13566                 cfg.cn[1].cls += ' col-xs-' + (12 - this.labelxs);
13567             }
13568             
13569         } else if ( this.fieldLabel.length) {
13570             cfg.cn = [
13571
13572                {
13573                    tag: 'label',
13574                    //cls : 'input-group-addon',
13575                    html : this.fieldLabel
13576
13577                },
13578
13579                inputblock
13580
13581            ];
13582
13583         } else {
13584
13585             cfg.cn = [
13586
13587                 inputblock
13588
13589             ];
13590                 
13591         }
13592         
13593         if (this.disabled) {
13594             input.disabled=true;
13595         }
13596         
13597         return cfg;
13598         
13599     },
13600     /**
13601      * return the real textarea element.
13602      */
13603     inputEl: function ()
13604     {
13605         return this.el.select('textarea.form-control',true).first();
13606     },
13607     
13608     /**
13609      * Clear any invalid styles/messages for this field
13610      */
13611     clearInvalid : function()
13612     {
13613         
13614         if(!this.el || this.preventMark){ // not rendered
13615             return;
13616         }
13617         
13618         var label = this.el.select('label', true).first();
13619         var icon = this.el.select('i.fa-star', true).first();
13620         
13621         if(label && icon){
13622             icon.remove();
13623         }
13624         this.el.removeClass( this.validClass);
13625         this.inputEl().removeClass('is-invalid');
13626          
13627         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
13628             
13629             var feedback = this.el.select('.form-control-feedback', true).first();
13630             
13631             if(feedback){
13632                 this.el.select('.form-control-feedback', true).first().removeClass(this.invalidFeedbackClass);
13633             }
13634             
13635         }
13636         
13637         this.fireEvent('valid', this);
13638     },
13639     
13640      /**
13641      * Mark this field as valid
13642      */
13643     markValid : function()
13644     {
13645         if(!this.el  || this.preventMark){ // not rendered
13646             return;
13647         }
13648         
13649         this.el.removeClass([this.invalidClass, this.validClass]);
13650         this.inputEl().removeClass(['is-valid', 'is-invalid']);
13651         
13652         var feedback = this.el.select('.form-control-feedback', true).first();
13653             
13654         if(feedback){
13655             this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
13656         }
13657
13658         if(this.disabled || this.allowBlank){
13659             return;
13660         }
13661         
13662         var label = this.el.select('label', true).first();
13663         var icon = this.el.select('i.fa-star', true).first();
13664         
13665         if(label && icon){
13666             icon.remove();
13667         }
13668         if (Roo.bootstrap.version == 3) {
13669             this.el.addClass(this.validClass);
13670         } else {
13671             this.inputEl().addClass('is-valid');
13672         }
13673         
13674         
13675         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank && (this.getValue().length || this.forceFeedback)){
13676             
13677             var feedback = this.el.select('.form-control-feedback', true).first();
13678             
13679             if(feedback){
13680                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
13681                 this.el.select('.form-control-feedback', true).first().addClass([this.validFeedbackClass]);
13682             }
13683             
13684         }
13685         
13686         this.fireEvent('valid', this);
13687     },
13688     
13689      /**
13690      * Mark this field as invalid
13691      * @param {String} msg The validation message
13692      */
13693     markInvalid : function(msg)
13694     {
13695         if(!this.el  || this.preventMark){ // not rendered
13696             return;
13697         }
13698         
13699         this.el.removeClass([this.invalidClass, this.validClass]);
13700         this.inputEl().removeClass(['is-valid', 'is-invalid']);
13701         
13702         var feedback = this.el.select('.form-control-feedback', true).first();
13703             
13704         if(feedback){
13705             this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
13706         }
13707
13708         if(this.disabled || this.allowBlank){
13709             return;
13710         }
13711         
13712         var label = this.el.select('label', true).first();
13713         var icon = this.el.select('i.fa-star', true).first();
13714         
13715         if(!this.getValue().length && label && !icon){
13716             this.el.createChild({
13717                 tag : 'i',
13718                 cls : 'text-danger fa fa-lg fa-star',
13719                 tooltip : 'This field is required',
13720                 style : 'margin-right:5px;'
13721             }, label, true);
13722         }
13723         
13724         if (Roo.bootstrap.version == 3) {
13725             this.el.addClass(this.invalidClass);
13726         } else {
13727             this.inputEl().addClass('is-invalid');
13728         }
13729         
13730         // fixme ... this may be depricated need to test..
13731         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
13732             
13733             var feedback = this.el.select('.form-control-feedback', true).first();
13734             
13735             if(feedback){
13736                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
13737                 
13738                 if(this.getValue().length || this.forceFeedback){
13739                     this.el.select('.form-control-feedback', true).first().addClass([this.invalidFeedbackClass]);
13740                 }
13741                 
13742             }
13743             
13744         }
13745         
13746         this.fireEvent('invalid', this, msg);
13747     }
13748 });
13749
13750  
13751 /*
13752  * - LGPL
13753  *
13754  * trigger field - base class for combo..
13755  * 
13756  */
13757  
13758 /**
13759  * @class Roo.bootstrap.form.TriggerField
13760  * @extends Roo.bootstrap.form.Input
13761  * Provides a convenient wrapper for TextFields that adds a clickable trigger button (looks like a combobox by default).
13762  * The trigger has no default action, so you must assign a function to implement the trigger click handler by
13763  * overriding {@link #onTriggerClick}. You can create a TriggerField directly, as it renders exactly like a combobox
13764  * for which you can provide a custom implementation.  For example:
13765  * <pre><code>
13766 var trigger = new Roo.bootstrap.form.TriggerField();
13767 trigger.onTriggerClick = myTriggerFn;
13768 trigger.applyTo('my-field');
13769 </code></pre>
13770  *
13771  * However, in general you will most likely want to use TriggerField as the base class for a reusable component.
13772  * {@link Roo.bootstrap.form.DateField} and {@link Roo.bootstrap.form.ComboBox} are perfect examples of this.
13773  * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
13774  * class 'x-form-trigger' by default and triggerClass will be <b>appended</b> if specified.
13775  * @cfg {String} caret (search|calendar) BS3 only - carat fa name
13776
13777  * @constructor
13778  * Create a new TriggerField.
13779  * @param {Object} config Configuration options (valid {@Roo.bootstrap.form.Input} config options will also be applied
13780  * to the base TextField)
13781  */
13782 Roo.bootstrap.form.TriggerField = function(config){
13783     this.mimicing = false;
13784     Roo.bootstrap.form.TriggerField.superclass.constructor.call(this, config);
13785 };
13786
13787 Roo.extend(Roo.bootstrap.form.TriggerField, Roo.bootstrap.form.Input,  {
13788     /**
13789      * @cfg {String} triggerClass A CSS class to apply to the trigger
13790      */
13791      /**
13792      * @cfg {Boolean} hideTrigger True to hide the trigger element and display only the base text field (defaults to false)
13793      */
13794     hideTrigger:false,
13795
13796     /**
13797      * @cfg {Boolean} removable (true|false) special filter default false
13798      */
13799     removable : false,
13800     
13801     /** @cfg {Boolean} grow @hide */
13802     /** @cfg {Number} growMin @hide */
13803     /** @cfg {Number} growMax @hide */
13804
13805     /**
13806      * @hide 
13807      * @method
13808      */
13809     autoSize: Roo.emptyFn,
13810     // private
13811     monitorTab : true,
13812     // private
13813     deferHeight : true,
13814
13815     
13816     actionMode : 'wrap',
13817     
13818     caret : false,
13819     
13820     
13821     getAutoCreate : function(){
13822        
13823         var align = this.labelAlign || this.parentLabelAlign();
13824         
13825         var id = Roo.id();
13826         
13827         var cfg = {
13828             cls: 'form-group' //input-group
13829         };
13830         
13831         
13832         var input =  {
13833             tag: 'input',
13834             id : id,
13835             type : this.inputType,
13836             cls : 'form-control',
13837             autocomplete: 'new-password',
13838             placeholder : this.placeholder || '' 
13839             
13840         };
13841         if (this.name) {
13842             input.name = this.name;
13843         }
13844         if (this.size) {
13845             input.cls += ' input-' + this.size;
13846         }
13847         
13848         if (this.disabled) {
13849             input.disabled=true;
13850         }
13851         
13852         var inputblock = input;
13853         
13854         if(this.hasFeedback && !this.allowBlank){
13855             
13856             var feedback = {
13857                 tag: 'span',
13858                 cls: 'glyphicon form-control-feedback'
13859             };
13860             
13861             if(this.removable && !this.editable  ){
13862                 inputblock = {
13863                     cls : 'has-feedback',
13864                     cn :  [
13865                         inputblock,
13866                         {
13867                             tag: 'button',
13868                             html : 'x',
13869                             cls : 'roo-combo-removable-btn close'
13870                         },
13871                         feedback
13872                     ] 
13873                 };
13874             } else {
13875                 inputblock = {
13876                     cls : 'has-feedback',
13877                     cn :  [
13878                         inputblock,
13879                         feedback
13880                     ] 
13881                 };
13882             }
13883
13884         } else {
13885             if(this.removable && !this.editable ){
13886                 inputblock = {
13887                     cls : 'roo-removable',
13888                     cn :  [
13889                         inputblock,
13890                         {
13891                             tag: 'button',
13892                             html : 'x',
13893                             cls : 'roo-combo-removable-btn close'
13894                         }
13895                     ] 
13896                 };
13897             }
13898         }
13899         
13900         if (this.before || this.after) {
13901             
13902             inputblock = {
13903                 cls : 'input-group',
13904                 cn :  [] 
13905             };
13906             if (this.before) {
13907                 inputblock.cn.push({
13908                     tag :'span',
13909                     cls : 'input-group-addon input-group-prepend input-group-text',
13910                     html : this.before
13911                 });
13912             }
13913             
13914             inputblock.cn.push(input);
13915             
13916             if(this.hasFeedback && !this.allowBlank){
13917                 inputblock.cls += ' has-feedback';
13918                 inputblock.cn.push(feedback);
13919             }
13920             
13921             if (this.after) {
13922                 inputblock.cn.push({
13923                     tag :'span',
13924                     cls : 'input-group-addon input-group-append input-group-text',
13925                     html : this.after
13926                 });
13927             }
13928             
13929         };
13930         
13931       
13932         
13933         var ibwrap = inputblock;
13934         
13935         if(this.multiple){
13936             ibwrap = {
13937                 tag: 'ul',
13938                 cls: 'roo-select2-choices',
13939                 cn:[
13940                     {
13941                         tag: 'li',
13942                         cls: 'roo-select2-search-field',
13943                         cn: [
13944
13945                             inputblock
13946                         ]
13947                     }
13948                 ]
13949             };
13950                 
13951         }
13952         
13953         var combobox = {
13954             cls: 'roo-select2-container input-group',
13955             cn: [
13956                  {
13957                     tag: 'input',
13958                     type : 'hidden',
13959                     cls: 'form-hidden-field'
13960                 },
13961                 ibwrap
13962             ]
13963         };
13964         
13965         if(!this.multiple && this.showToggleBtn){
13966             
13967             var caret = {
13968                         tag: 'span',
13969                         cls: 'caret'
13970              };
13971             if (this.caret != false) {
13972                 caret = {
13973                      tag: 'i',
13974                      cls: 'fa fa-' + this.caret
13975                 };
13976                 
13977             }
13978             
13979             combobox.cn.push({
13980                 tag :'span',
13981                 cls : 'input-group-addon input-group-append input-group-text btn dropdown-toggle',
13982                 cn : [
13983                     Roo.bootstrap.version == 3 ? caret : '',
13984                     {
13985                         tag: 'span',
13986                         cls: 'combobox-clear',
13987                         cn  : [
13988                             {
13989                                 tag : 'i',
13990                                 cls: 'icon-remove'
13991                             }
13992                         ]
13993                     }
13994                 ]
13995
13996             })
13997         }
13998         
13999         if(this.multiple){
14000             combobox.cls += ' roo-select2-container-multi';
14001         }
14002          var indicator = {
14003             tag : 'i',
14004             cls : 'roo-required-indicator ' + (this.indicatorpos == 'right'  ? 'right' : 'left') +'-indicator text-danger fa fa-lg fa-star',
14005             tooltip : 'This field is required'
14006         };
14007         if (Roo.bootstrap.version == 4) {
14008             indicator = {
14009                 tag : 'i',
14010                 style : 'display:none'
14011             };
14012         }
14013         
14014         
14015         if (align ==='left' && this.fieldLabel.length) {
14016             
14017             cfg.cls += ' roo-form-group-label-left'  + (Roo.bootstrap.version == 4 ? ' row' : '');
14018
14019             cfg.cn = [
14020                 indicator,
14021                 {
14022                     tag: 'label',
14023                     'for' :  id,
14024                     cls : 'control-label',
14025                     html : this.fieldLabel
14026
14027                 },
14028                 {
14029                     cls : "", 
14030                     cn: [
14031                         combobox
14032                     ]
14033                 }
14034
14035             ];
14036             
14037             var labelCfg = cfg.cn[1];
14038             var contentCfg = cfg.cn[2];
14039             
14040             if(this.indicatorpos == 'right'){
14041                 cfg.cn = [
14042                     {
14043                         tag: 'label',
14044                         'for' :  id,
14045                         cls : 'control-label',
14046                         cn : [
14047                             {
14048                                 tag : 'span',
14049                                 html : this.fieldLabel
14050                             },
14051                             indicator
14052                         ]
14053                     },
14054                     {
14055                         cls : "", 
14056                         cn: [
14057                             combobox
14058                         ]
14059                     }
14060
14061                 ];
14062                 
14063                 labelCfg = cfg.cn[0];
14064                 contentCfg = cfg.cn[1];
14065             }
14066             
14067             if(this.labelWidth > 12){
14068                 labelCfg.style = "width: " + this.labelWidth + 'px';
14069             }
14070             
14071             if(this.labelWidth < 13 && this.labelmd == 0){
14072                 this.labelmd = this.labelWidth;
14073             }
14074             
14075             if(this.labellg > 0){
14076                 labelCfg.cls += ' col-lg-' + this.labellg;
14077                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
14078             }
14079             
14080             if(this.labelmd > 0){
14081                 labelCfg.cls += ' col-md-' + this.labelmd;
14082                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
14083             }
14084             
14085             if(this.labelsm > 0){
14086                 labelCfg.cls += ' col-sm-' + this.labelsm;
14087                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
14088             }
14089             
14090             if(this.labelxs > 0){
14091                 labelCfg.cls += ' col-xs-' + this.labelxs;
14092                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
14093             }
14094             
14095         } else if ( this.fieldLabel.length) {
14096 //                Roo.log(" label");
14097             cfg.cn = [
14098                 indicator,
14099                {
14100                    tag: 'label',
14101                    //cls : 'input-group-addon',
14102                    html : this.fieldLabel
14103
14104                },
14105
14106                combobox
14107
14108             ];
14109             
14110             if(this.indicatorpos == 'right'){
14111                 
14112                 cfg.cn = [
14113                     {
14114                        tag: 'label',
14115                        cn : [
14116                            {
14117                                tag : 'span',
14118                                html : this.fieldLabel
14119                            },
14120                            indicator
14121                        ]
14122
14123                     },
14124                     combobox
14125
14126                 ];
14127
14128             }
14129
14130         } else {
14131             
14132 //                Roo.log(" no label && no align");
14133                 cfg = combobox
14134                      
14135                 
14136         }
14137         
14138         var settings=this;
14139         ['xs','sm','md','lg'].map(function(size){
14140             if (settings[size]) {
14141                 cfg.cls += ' col-' + size + '-' + settings[size];
14142             }
14143         });
14144         
14145         return cfg;
14146         
14147     },
14148     
14149     
14150     
14151     // private
14152     onResize : function(w, h){
14153 //        Roo.bootstrap.form.TriggerField.superclass.onResize.apply(this, arguments);
14154 //        if(typeof w == 'number'){
14155 //            var x = w - this.trigger.getWidth();
14156 //            this.inputEl().setWidth(this.adjustWidth('input', x));
14157 //            this.trigger.setStyle('left', x+'px');
14158 //        }
14159     },
14160
14161     // private
14162     adjustSize : Roo.BoxComponent.prototype.adjustSize,
14163
14164     // private
14165     getResizeEl : function(){
14166         return this.inputEl();
14167     },
14168
14169     // private
14170     getPositionEl : function(){
14171         return this.inputEl();
14172     },
14173
14174     // private
14175     alignErrorIcon : function(){
14176         this.errorIcon.alignTo(this.inputEl(), 'tl-tr', [2, 0]);
14177     },
14178
14179     // private
14180     initEvents : function(){
14181         
14182         this.createList();
14183         
14184         Roo.bootstrap.form.TriggerField.superclass.initEvents.call(this);
14185         //this.wrap = this.el.wrap({cls: "x-form-field-wrap"});
14186         if(!this.multiple && this.showToggleBtn){
14187             this.trigger = this.el.select('span.dropdown-toggle',true).first();
14188             if(this.hideTrigger){
14189                 this.trigger.setDisplayed(false);
14190             }
14191             this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
14192         }
14193         
14194         if(this.multiple){
14195             this.inputEl().on("click", this.onTriggerClick, this, {preventDefault:true});
14196         }
14197         
14198         if(this.removable && !this.editable && !this.tickable){
14199             var close = this.closeTriggerEl();
14200             
14201             if(close){
14202                 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
14203                 close.on('click', this.removeBtnClick, this, close);
14204             }
14205         }
14206         
14207         //this.trigger.addClassOnOver('x-form-trigger-over');
14208         //this.trigger.addClassOnClick('x-form-trigger-click');
14209         
14210         //if(!this.width){
14211         //    this.wrap.setWidth(this.el.getWidth()+this.trigger.getWidth());
14212         //}
14213     },
14214     
14215     closeTriggerEl : function()
14216     {
14217         var close = this.el.select('.roo-combo-removable-btn', true).first();
14218         return close ? close : false;
14219     },
14220     
14221     removeBtnClick : function(e, h, el)
14222     {
14223         e.preventDefault();
14224         
14225         if(this.fireEvent("remove", this) !== false){
14226             this.reset();
14227             this.fireEvent("afterremove", this)
14228         }
14229     },
14230     
14231     createList : function()
14232     {
14233         this.list = Roo.get(document.body).createChild({
14234             tag: Roo.bootstrap.version == 4 ? 'div' : 'ul',
14235             cls: 'typeahead typeahead-long dropdown-menu shadow',
14236             style: 'display:none'
14237         });
14238         
14239         this.list.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';;
14240         
14241     },
14242
14243     // private
14244     initTrigger : function(){
14245        
14246     },
14247
14248     // private
14249     onDestroy : function(){
14250         if(this.trigger){
14251             this.trigger.removeAllListeners();
14252           //  this.trigger.remove();
14253         }
14254         //if(this.wrap){
14255         //    this.wrap.remove();
14256         //}
14257         Roo.bootstrap.form.TriggerField.superclass.onDestroy.call(this);
14258     },
14259
14260     // private
14261     onFocus : function(){
14262         Roo.bootstrap.form.TriggerField.superclass.onFocus.call(this);
14263         /*
14264         if(!this.mimicing){
14265             this.wrap.addClass('x-trigger-wrap-focus');
14266             this.mimicing = true;
14267             Roo.get(Roo.isIE ? document.body : document).on("mousedown", this.mimicBlur, this);
14268             if(this.monitorTab){
14269                 this.el.on("keydown", this.checkTab, this);
14270             }
14271         }
14272         */
14273     },
14274
14275     // private
14276     checkTab : function(e){
14277         if(e.getKey() == e.TAB){
14278             this.triggerBlur();
14279         }
14280     },
14281
14282     // private
14283     onBlur : function(){
14284         // do nothing
14285     },
14286
14287     // private
14288     mimicBlur : function(e, t){
14289         /*
14290         if(!this.wrap.contains(t) && this.validateBlur()){
14291             this.triggerBlur();
14292         }
14293         */
14294     },
14295
14296     // private
14297     triggerBlur : function(){
14298         this.mimicing = false;
14299         Roo.get(Roo.isIE ? document.body : document).un("mousedown", this.mimicBlur);
14300         if(this.monitorTab){
14301             this.el.un("keydown", this.checkTab, this);
14302         }
14303         //this.wrap.removeClass('x-trigger-wrap-focus');
14304         Roo.bootstrap.form.TriggerField.superclass.onBlur.call(this);
14305     },
14306
14307     // private
14308     // This should be overriden by any subclass that needs to check whether or not the field can be blurred.
14309     validateBlur : function(e, t){
14310         return true;
14311     },
14312
14313     // private
14314     onDisable : function(){
14315         this.inputEl().dom.disabled = true;
14316         //Roo.bootstrap.form.TriggerField.superclass.onDisable.call(this);
14317         //if(this.wrap){
14318         //    this.wrap.addClass('x-item-disabled');
14319         //}
14320     },
14321
14322     // private
14323     onEnable : function(){
14324         this.inputEl().dom.disabled = false;
14325         //Roo.bootstrap.form.TriggerField.superclass.onEnable.call(this);
14326         //if(this.wrap){
14327         //    this.el.removeClass('x-item-disabled');
14328         //}
14329     },
14330
14331     // private
14332     onShow : function(){
14333         var ae = this.getActionEl();
14334         
14335         if(ae){
14336             ae.dom.style.display = '';
14337             ae.dom.style.visibility = 'visible';
14338         }
14339     },
14340
14341     // private
14342     
14343     onHide : function(){
14344         var ae = this.getActionEl();
14345         ae.dom.style.display = 'none';
14346     },
14347
14348     /**
14349      * The function that should handle the trigger's click event.  This method does nothing by default until overridden
14350      * by an implementing function.
14351      * @method
14352      * @param {EventObject} e
14353      */
14354     onTriggerClick : Roo.emptyFn
14355 });
14356  
14357 /*
14358 * Licence: LGPL
14359 */
14360
14361 /**
14362  * @class Roo.bootstrap.form.CardUploader
14363  * @extends Roo.bootstrap.Button
14364  * Bootstrap Card Uploader class - it's a button which when you add files to it, adds cards below with preview and the name...
14365  * @cfg {Number} errorTimeout default 3000
14366  * @cfg {Array}  images  an array of ?? Img objects ??? when loading existing files..
14367  * @cfg {Array}  html The button text.
14368
14369  *
14370  * @constructor
14371  * Create a new CardUploader
14372  * @param {Object} config The config object
14373  */
14374
14375 Roo.bootstrap.form.CardUploader = function(config){
14376     
14377  
14378     
14379     Roo.bootstrap.form.CardUploader.superclass.constructor.call(this, config);
14380     
14381     
14382     this.fileCollection   = new Roo.util.MixedCollection(false,function(r) {
14383         return r.data.id
14384      });
14385     
14386      this.addEvents({
14387          // raw events
14388         /**
14389          * @event preview
14390          * When a image is clicked on - and needs to display a slideshow or similar..
14391          * @param {Roo.bootstrap.Card} this
14392          * @param {Object} The image information data 
14393          *
14394          */
14395         'preview' : true,
14396          /**
14397          * @event download
14398          * When a the download link is clicked
14399          * @param {Roo.bootstrap.Card} this
14400          * @param {Object} The image information data  contains 
14401          */
14402         'download' : true
14403         
14404     });
14405 };
14406  
14407 Roo.extend(Roo.bootstrap.form.CardUploader, Roo.bootstrap.form.Input,  {
14408     
14409      
14410     errorTimeout : 3000,
14411      
14412     images : false,
14413    
14414     fileCollection : false,
14415     allowBlank : true,
14416     
14417     getAutoCreate : function()
14418     {
14419         
14420         var cfg =  {
14421             cls :'form-group' ,
14422             cn : [
14423                
14424                 {
14425                     tag: 'label',
14426                    //cls : 'input-group-addon',
14427                     html : this.fieldLabel
14428
14429                 },
14430
14431                 {
14432                     tag: 'input',
14433                     type : 'hidden',
14434                     name : this.name,
14435                     value : this.value,
14436                     cls : 'd-none  form-control'
14437                 },
14438                 
14439                 {
14440                     tag: 'input',
14441                     multiple : 'multiple',
14442                     type : 'file',
14443                     cls : 'd-none  roo-card-upload-selector'
14444                 },
14445                 
14446                 {
14447                     cls : 'roo-card-uploader-button-container w-100 mb-2'
14448                 },
14449                 {
14450                     cls : 'card-columns roo-card-uploader-container'
14451                 }
14452
14453             ]
14454         };
14455            
14456          
14457         return cfg;
14458     },
14459     
14460     getChildContainer : function() /// what children are added to.
14461     {
14462         return this.containerEl;
14463     },
14464    
14465     getButtonContainer : function() /// what children are added to.
14466     {
14467         return this.el.select(".roo-card-uploader-button-container").first();
14468     },
14469    
14470     initEvents : function()
14471     {
14472         
14473         Roo.bootstrap.form.Input.prototype.initEvents.call(this);
14474         
14475         var t = this;
14476         this.addxtype({
14477             xns: Roo.bootstrap,
14478
14479             xtype : 'Button',
14480             container_method : 'getButtonContainer' ,            
14481             html :  this.html, // fix changable?
14482             cls : 'w-100 ',
14483             listeners : {
14484                 'click' : function(btn, e) {
14485                     t.onClick(e);
14486                 }
14487             }
14488         });
14489         
14490         
14491         
14492         
14493         this.urlAPI = (window.createObjectURL && window) || 
14494                                 (window.URL && URL.revokeObjectURL && URL) || 
14495                                 (window.webkitURL && webkitURL);
14496                         
14497          
14498          
14499          
14500         this.selectorEl = this.el.select('.roo-card-upload-selector', true).first();
14501         
14502         this.selectorEl.on('change', this.onFileSelected, this);
14503         if (this.images) {
14504             var t = this;
14505             this.images.forEach(function(img) {
14506                 t.addCard(img)
14507             });
14508             this.images = false;
14509         }
14510         this.containerEl = this.el.select('.roo-card-uploader-container', true).first();
14511          
14512        
14513     },
14514     
14515    
14516     onClick : function(e)
14517     {
14518         e.preventDefault();
14519          
14520         this.selectorEl.dom.click();
14521          
14522     },
14523     
14524     onFileSelected : function(e)
14525     {
14526         e.preventDefault();
14527         
14528         if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
14529             return;
14530         }
14531         
14532         Roo.each(this.selectorEl.dom.files, function(file){    
14533             this.addFile(file);
14534         }, this);
14535          
14536     },
14537     
14538       
14539     
14540       
14541     
14542     addFile : function(file)
14543     {
14544            
14545         if(typeof(file) === 'string'){
14546             throw "Add file by name?"; // should not happen
14547             return;
14548         }
14549         
14550         if(!file || !this.urlAPI){
14551             return;
14552         }
14553         
14554         // file;
14555         // file.type;
14556         
14557         var _this = this;
14558         
14559         
14560         var url = _this.urlAPI.createObjectURL( file);
14561            
14562         this.addCard({
14563             id : Roo.bootstrap.form.CardUploader.ID--,
14564             is_uploaded : false,
14565             src : url,
14566             srcfile : file,
14567             title : file.name,
14568             mimetype : file.type,
14569             preview : false,
14570             is_deleted : 0
14571         });
14572         
14573     },
14574     
14575     /**
14576      * addCard - add an Attachment to the uploader
14577      * @param data - the data about the image to upload
14578      *
14579      * {
14580           id : 123
14581           title : "Title of file",
14582           is_uploaded : false,
14583           src : "http://.....",
14584           srcfile : { the File upload object },
14585           mimetype : file.type,
14586           preview : false,
14587           is_deleted : 0
14588           .. any other data...
14589         }
14590      *
14591      * 
14592     */
14593     
14594     addCard : function (data)
14595     {
14596         // hidden input element?
14597         // if the file is not an image...
14598         //then we need to use something other that and header_image
14599         var t = this;
14600         //   remove.....
14601         var footer = [
14602             {
14603                 xns : Roo.bootstrap,
14604                 xtype : 'CardFooter',
14605                  items: [
14606                     {
14607                         xns : Roo.bootstrap,
14608                         xtype : 'Element',
14609                         cls : 'd-flex',
14610                         items : [
14611                             
14612                             {
14613                                 xns : Roo.bootstrap,
14614                                 xtype : 'Button',
14615                                 html : String.format("<small>{0}</small>", data.title),
14616                                 cls : 'col-10 text-left',
14617                                 size: 'sm',
14618                                 weight: 'link',
14619                                 fa : 'download',
14620                                 listeners : {
14621                                     click : function() {
14622                                      
14623                                         t.fireEvent( "download", t, data );
14624                                     }
14625                                 }
14626                             },
14627                           
14628                             {
14629                                 xns : Roo.bootstrap,
14630                                 xtype : 'Button',
14631                                 style: 'max-height: 28px; ',
14632                                 size : 'sm',
14633                                 weight: 'danger',
14634                                 cls : 'col-2',
14635                                 fa : 'times',
14636                                 listeners : {
14637                                     click : function() {
14638                                         t.removeCard(data.id)
14639                                     }
14640                                 }
14641                             }
14642                         ]
14643                     }
14644                     
14645                 ] 
14646             }
14647             
14648         ];
14649         
14650         var cn = this.addxtype(
14651             {
14652                  
14653                 xns : Roo.bootstrap,
14654                 xtype : 'Card',
14655                 closeable : true,
14656                 header : !data.mimetype.match(/image/) && !data.preview ? "Document": false,
14657                 header_image : data.mimetype.match(/image/) ? data.src  : data.preview,
14658                 header_image_fit_square: true, // fixme  - we probably need to use the 'Img' element to do stuff like this.
14659                 data : data,
14660                 html : false,
14661                  
14662                 items : footer,
14663                 initEvents : function() {
14664                     Roo.bootstrap.Card.prototype.initEvents.call(this);
14665                     var card = this;
14666                     this.imgEl = this.el.select('.card-img-top').first();
14667                     if (this.imgEl) {
14668                         this.imgEl.on('click', function() { t.fireEvent( "preview", t, data ); }, this);
14669                         this.imgEl.set({ 'pointer' : 'cursor' });
14670                                   
14671                     }
14672                     this.getCardFooter().addClass('p-1');
14673                     
14674                   
14675                 }
14676                 
14677             }
14678         );
14679         // dont' really need ot update items.
14680         // this.items.push(cn);
14681         this.fileCollection.add(cn);
14682         
14683         if (!data.srcfile) {
14684             this.updateInput();
14685             return;
14686         }
14687             
14688         var _t = this;
14689         var reader = new FileReader();
14690         reader.addEventListener("load", function() {  
14691             data.srcdata =  reader.result;
14692             _t.updateInput();
14693         });
14694         reader.readAsDataURL(data.srcfile);
14695         
14696         
14697         
14698     },
14699     removeCard : function(id)
14700     {
14701         
14702         var card  = this.fileCollection.get(id);
14703         card.data.is_deleted = 1;
14704         card.data.src = ''; /// delete the source - so it reduces size of not uploaded images etc.
14705         //this.fileCollection.remove(card);
14706         //this.items = this.items.filter(function(e) { return e != card });
14707         // dont' really need ot update items.
14708         card.el.dom.parentNode.removeChild(card.el.dom);
14709         this.updateInput();
14710
14711         
14712     },
14713     reset: function()
14714     {
14715         this.fileCollection.each(function(card) {
14716             if (card.el.dom && card.el.dom.parentNode) {
14717                 card.el.dom.parentNode.removeChild(card.el.dom);
14718             }
14719         });
14720         this.fileCollection.clear();
14721         this.updateInput();
14722     },
14723     
14724     updateInput : function()
14725     {
14726          var data = [];
14727         this.fileCollection.each(function(e) {
14728             data.push(e.data);
14729             
14730         });
14731         this.inputEl().dom.value = JSON.stringify(data);
14732         
14733         
14734         
14735     }
14736     
14737     
14738 });
14739
14740
14741 Roo.bootstrap.form.CardUploader.ID = -1;/*
14742  * Based on:
14743  * Ext JS Library 1.1.1
14744  * Copyright(c) 2006-2007, Ext JS, LLC.
14745  *
14746  * Originally Released Under LGPL - original licence link has changed is not relivant.
14747  *
14748  * Fork - LGPL
14749  * <script type="text/javascript">
14750  */
14751
14752
14753 /**
14754  * @class Roo.data.SortTypes
14755  * @static
14756  * Defines the default sorting (casting?) comparison functions used when sorting data.
14757  */
14758 Roo.data.SortTypes = {
14759     /**
14760      * Default sort that does nothing
14761      * @param {Mixed} s The value being converted
14762      * @return {Mixed} The comparison value
14763      */
14764     none : function(s){
14765         return s;
14766     },
14767     
14768     /**
14769      * The regular expression used to strip tags
14770      * @type {RegExp}
14771      * @property
14772      */
14773     stripTagsRE : /<\/?[^>]+>/gi,
14774     
14775     /**
14776      * Strips all HTML tags to sort on text only
14777      * @param {Mixed} s The value being converted
14778      * @return {String} The comparison value
14779      */
14780     asText : function(s){
14781         return String(s).replace(this.stripTagsRE, "");
14782     },
14783     
14784     /**
14785      * Strips all HTML tags to sort on text only - Case insensitive
14786      * @param {Mixed} s The value being converted
14787      * @return {String} The comparison value
14788      */
14789     asUCText : function(s){
14790         return String(s).toUpperCase().replace(this.stripTagsRE, "");
14791     },
14792     
14793     /**
14794      * Case insensitive string
14795      * @param {Mixed} s The value being converted
14796      * @return {String} The comparison value
14797      */
14798     asUCString : function(s) {
14799         return String(s).toUpperCase();
14800     },
14801     
14802     /**
14803      * Date sorting
14804      * @param {Mixed} s The value being converted
14805      * @return {Number} The comparison value
14806      */
14807     asDate : function(s) {
14808         if(!s){
14809             return 0;
14810         }
14811         if(s instanceof Date){
14812             return s.getTime();
14813         }
14814         return Date.parse(String(s));
14815     },
14816     
14817     /**
14818      * Float sorting
14819      * @param {Mixed} s The value being converted
14820      * @return {Float} The comparison value
14821      */
14822     asFloat : function(s) {
14823         var val = parseFloat(String(s).replace(/,/g, ""));
14824         if(isNaN(val)) {
14825             val = 0;
14826         }
14827         return val;
14828     },
14829     
14830     /**
14831      * Integer sorting
14832      * @param {Mixed} s The value being converted
14833      * @return {Number} The comparison value
14834      */
14835     asInt : function(s) {
14836         var val = parseInt(String(s).replace(/,/g, ""));
14837         if(isNaN(val)) {
14838             val = 0;
14839         }
14840         return val;
14841     }
14842 };/*
14843  * Based on:
14844  * Ext JS Library 1.1.1
14845  * Copyright(c) 2006-2007, Ext JS, LLC.
14846  *
14847  * Originally Released Under LGPL - original licence link has changed is not relivant.
14848  *
14849  * Fork - LGPL
14850  * <script type="text/javascript">
14851  */
14852
14853 /**
14854 * @class Roo.data.Record
14855  * Instances of this class encapsulate both record <em>definition</em> information, and record
14856  * <em>value</em> information for use in {@link Roo.data.Store} objects, or any code which needs
14857  * to access Records cached in an {@link Roo.data.Store} object.<br>
14858  * <p>
14859  * Constructors for this class are generated by passing an Array of field definition objects to {@link #create}.
14860  * Instances are usually only created by {@link Roo.data.Reader} implementations when processing unformatted data
14861  * objects.<br>
14862  * <p>
14863  * Record objects generated by this constructor inherit all the methods of Roo.data.Record listed below.
14864  * @constructor
14865  * This constructor should not be used to create Record objects. Instead, use the constructor generated by
14866  * {@link #create}. The parameters are the same.
14867  * @param {Array} data An associative Array of data values keyed by the field name.
14868  * @param {Object} id (Optional) The id of the record. This id should be unique, and is used by the
14869  * {@link Roo.data.Store} object which owns the Record to index its collection of Records. If
14870  * not specified an integer id is generated.
14871  */
14872 Roo.data.Record = function(data, id){
14873     this.id = (id || id === 0) ? id : ++Roo.data.Record.AUTO_ID;
14874     this.data = data;
14875 };
14876
14877 /**
14878  * Generate a constructor for a specific record layout.
14879  * @param {Array} o An Array of field definition objects which specify field names, and optionally,
14880  * data types, and a mapping for an {@link Roo.data.Reader} to extract the field's value from a data object.
14881  * Each field definition object may contain the following properties: <ul>
14882  * <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,
14883  * for example the <em>dataIndex</em> property in column definition objects passed to {@link Roo.grid.ColumnModel}</p></li>
14884  * <li><b>mapping</b> : String<p style="margin-left:1em">(Optional) A path specification for use by the {@link Roo.data.Reader} implementation
14885  * that is creating the Record to access the data value from the data object. If an {@link Roo.data.JsonReader}
14886  * is being used, then this is a string containing the javascript expression to reference the data relative to 
14887  * the record item's root. If an {@link Roo.data.XmlReader} is being used, this is an {@link Roo.DomQuery} path
14888  * to the data item relative to the record element. If the mapping expression is the same as the field name,
14889  * this may be omitted.</p></li>
14890  * <li><b>type</b> : String<p style="margin-left:1em">(Optional) The data type for conversion to displayable value. Possible values are
14891  * <ul><li>auto (Default, implies no conversion)</li>
14892  * <li>string</li>
14893  * <li>int</li>
14894  * <li>float</li>
14895  * <li>boolean</li>
14896  * <li>date</li></ul></p></li>
14897  * <li><b>sortType</b> : Mixed<p style="margin-left:1em">(Optional) A member of {@link Roo.data.SortTypes}.</p></li>
14898  * <li><b>sortDir</b> : String<p style="margin-left:1em">(Optional) Initial direction to sort. "ASC" or "DESC"</p></li>
14899  * <li><b>convert</b> : Function<p style="margin-left:1em">(Optional) A function which converts the value provided
14900  * by the Reader into an object that will be stored in the Record. It is passed the
14901  * following parameters:<ul>
14902  * <li><b>v</b> : Mixed<p style="margin-left:1em">The data value as read by the Reader.</p></li>
14903  * </ul></p></li>
14904  * <li><b>dateFormat</b> : String<p style="margin-left:1em">(Optional) A format String for the Date.parseDate function.</p></li>
14905  * </ul>
14906  * <br>usage:<br><pre><code>
14907 var TopicRecord = Roo.data.Record.create(
14908     {name: 'title', mapping: 'topic_title'},
14909     {name: 'author', mapping: 'username'},
14910     {name: 'totalPosts', mapping: 'topic_replies', type: 'int'},
14911     {name: 'lastPost', mapping: 'post_time', type: 'date'},
14912     {name: 'lastPoster', mapping: 'user2'},
14913     {name: 'excerpt', mapping: 'post_text'}
14914 );
14915
14916 var myNewRecord = new TopicRecord({
14917     title: 'Do my job please',
14918     author: 'noobie',
14919     totalPosts: 1,
14920     lastPost: new Date(),
14921     lastPoster: 'Animal',
14922     excerpt: 'No way dude!'
14923 });
14924 myStore.add(myNewRecord);
14925 </code></pre>
14926  * @method create
14927  * @static
14928  */
14929 Roo.data.Record.create = function(o){
14930     var f = function(){
14931         f.superclass.constructor.apply(this, arguments);
14932     };
14933     Roo.extend(f, Roo.data.Record);
14934     var p = f.prototype;
14935     p.fields = new Roo.util.MixedCollection(false, function(field){
14936         return field.name;
14937     });
14938     for(var i = 0, len = o.length; i < len; i++){
14939         p.fields.add(new Roo.data.Field(o[i]));
14940     }
14941     f.getField = function(name){
14942         return p.fields.get(name);  
14943     };
14944     return f;
14945 };
14946
14947 Roo.data.Record.AUTO_ID = 1000;
14948 Roo.data.Record.EDIT = 'edit';
14949 Roo.data.Record.REJECT = 'reject';
14950 Roo.data.Record.COMMIT = 'commit';
14951
14952 Roo.data.Record.prototype = {
14953     /**
14954      * Readonly flag - true if this record has been modified.
14955      * @type Boolean
14956      */
14957     dirty : false,
14958     editing : false,
14959     error: null,
14960     modified: null,
14961
14962     // private
14963     join : function(store){
14964         this.store = store;
14965     },
14966
14967     /**
14968      * Set the named field to the specified value.
14969      * @param {String} name The name of the field to set.
14970      * @param {Object} value The value to set the field to.
14971      */
14972     set : function(name, value){
14973         if(this.data[name] == value){
14974             return;
14975         }
14976         this.dirty = true;
14977         if(!this.modified){
14978             this.modified = {};
14979         }
14980         if(typeof this.modified[name] == 'undefined'){
14981             this.modified[name] = this.data[name];
14982         }
14983         this.data[name] = value;
14984         if(!this.editing && this.store){
14985             this.store.afterEdit(this);
14986         }       
14987     },
14988
14989     /**
14990      * Get the value of the named field.
14991      * @param {String} name The name of the field to get the value of.
14992      * @return {Object} The value of the field.
14993      */
14994     get : function(name){
14995         return this.data[name]; 
14996     },
14997
14998     // private
14999     beginEdit : function(){
15000         this.editing = true;
15001         this.modified = {}; 
15002     },
15003
15004     // private
15005     cancelEdit : function(){
15006         this.editing = false;
15007         delete this.modified;
15008     },
15009
15010     // private
15011     endEdit : function(){
15012         this.editing = false;
15013         if(this.dirty && this.store){
15014             this.store.afterEdit(this);
15015         }
15016     },
15017
15018     /**
15019      * Usually called by the {@link Roo.data.Store} which owns the Record.
15020      * Rejects all changes made to the Record since either creation, or the last commit operation.
15021      * Modified fields are reverted to their original values.
15022      * <p>
15023      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
15024      * of reject operations.
15025      */
15026     reject : function(){
15027         var m = this.modified;
15028         for(var n in m){
15029             if(typeof m[n] != "function"){
15030                 this.data[n] = m[n];
15031             }
15032         }
15033         this.dirty = false;
15034         delete this.modified;
15035         this.editing = false;
15036         if(this.store){
15037             this.store.afterReject(this);
15038         }
15039     },
15040
15041     /**
15042      * Usually called by the {@link Roo.data.Store} which owns the Record.
15043      * Commits all changes made to the Record since either creation, or the last commit operation.
15044      * <p>
15045      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
15046      * of commit operations.
15047      */
15048     commit : function(){
15049         this.dirty = false;
15050         delete this.modified;
15051         this.editing = false;
15052         if(this.store){
15053             this.store.afterCommit(this);
15054         }
15055     },
15056
15057     // private
15058     hasError : function(){
15059         return this.error != null;
15060     },
15061
15062     // private
15063     clearError : function(){
15064         this.error = null;
15065     },
15066
15067     /**
15068      * Creates a copy of this record.
15069      * @param {String} id (optional) A new record id if you don't want to use this record's id
15070      * @return {Record}
15071      */
15072     copy : function(newId) {
15073         return new this.constructor(Roo.apply({}, this.data), newId || this.id);
15074     }
15075 };/*
15076  * Based on:
15077  * Ext JS Library 1.1.1
15078  * Copyright(c) 2006-2007, Ext JS, LLC.
15079  *
15080  * Originally Released Under LGPL - original licence link has changed is not relivant.
15081  *
15082  * Fork - LGPL
15083  * <script type="text/javascript">
15084  */
15085
15086
15087
15088 /**
15089  * @class Roo.data.Store
15090  * @extends Roo.util.Observable
15091  * The Store class encapsulates a client side cache of {@link Roo.data.Record} objects which provide input data
15092  * for widgets such as the Roo.grid.Grid, or the Roo.form.ComboBox.<br>
15093  * <p>
15094  * 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
15095  * has no knowledge of the format of the data returned by the Proxy.<br>
15096  * <p>
15097  * A Store object uses its configured implementation of {@link Roo.data.DataReader} to create {@link Roo.data.Record}
15098  * instances from the data object. These records are cached and made available through accessor functions.
15099  * @constructor
15100  * Creates a new Store.
15101  * @param {Object} config A config object containing the objects needed for the Store to access data,
15102  * and read the data into Records.
15103  */
15104 Roo.data.Store = function(config){
15105     this.data = new Roo.util.MixedCollection(false);
15106     this.data.getKey = function(o){
15107         return o.id;
15108     };
15109     this.baseParams = {};
15110     // private
15111     this.paramNames = {
15112         "start" : "start",
15113         "limit" : "limit",
15114         "sort" : "sort",
15115         "dir" : "dir",
15116         "multisort" : "_multisort"
15117     };
15118
15119     if(config && config.data){
15120         this.inlineData = config.data;
15121         delete config.data;
15122     }
15123
15124     Roo.apply(this, config);
15125     
15126     if(this.reader){ // reader passed
15127         this.reader = Roo.factory(this.reader, Roo.data);
15128         this.reader.xmodule = this.xmodule || false;
15129         if(!this.recordType){
15130             this.recordType = this.reader.recordType;
15131         }
15132         if(this.reader.onMetaChange){
15133             this.reader.onMetaChange = this.onMetaChange.createDelegate(this);
15134         }
15135     }
15136
15137     if(this.recordType){
15138         this.fields = this.recordType.prototype.fields;
15139     }
15140     this.modified = [];
15141
15142     this.addEvents({
15143         /**
15144          * @event datachanged
15145          * Fires when the data cache has changed, and a widget which is using this Store
15146          * as a Record cache should refresh its view.
15147          * @param {Store} this
15148          */
15149         datachanged : true,
15150         /**
15151          * @event metachange
15152          * Fires when this store's reader provides new metadata (fields). This is currently only support for JsonReaders.
15153          * @param {Store} this
15154          * @param {Object} meta The JSON metadata
15155          */
15156         metachange : true,
15157         /**
15158          * @event add
15159          * Fires when Records have been added to the Store
15160          * @param {Store} this
15161          * @param {Roo.data.Record[]} records The array of Records added
15162          * @param {Number} index The index at which the record(s) were added
15163          */
15164         add : true,
15165         /**
15166          * @event remove
15167          * Fires when a Record has been removed from the Store
15168          * @param {Store} this
15169          * @param {Roo.data.Record} record The Record that was removed
15170          * @param {Number} index The index at which the record was removed
15171          */
15172         remove : true,
15173         /**
15174          * @event update
15175          * Fires when a Record has been updated
15176          * @param {Store} this
15177          * @param {Roo.data.Record} record The Record that was updated
15178          * @param {String} operation The update operation being performed.  Value may be one of:
15179          * <pre><code>
15180  Roo.data.Record.EDIT
15181  Roo.data.Record.REJECT
15182  Roo.data.Record.COMMIT
15183          * </code></pre>
15184          */
15185         update : true,
15186         /**
15187          * @event clear
15188          * Fires when the data cache has been cleared.
15189          * @param {Store} this
15190          */
15191         clear : true,
15192         /**
15193          * @event beforeload
15194          * Fires before a request is made for a new data object.  If the beforeload handler returns false
15195          * the load action will be canceled.
15196          * @param {Store} this
15197          * @param {Object} options The loading options that were specified (see {@link #load} for details)
15198          */
15199         beforeload : true,
15200         /**
15201          * @event beforeloadadd
15202          * Fires after a new set of Records has been loaded.
15203          * @param {Store} this
15204          * @param {Roo.data.Record[]} records The Records that were loaded
15205          * @param {Object} options The loading options that were specified (see {@link #load} for details)
15206          */
15207         beforeloadadd : true,
15208         /**
15209          * @event load
15210          * Fires after a new set of Records has been loaded, before they are added to the store.
15211          * @param {Store} this
15212          * @param {Roo.data.Record[]} records The Records that were loaded
15213          * @param {Object} options The loading options that were specified (see {@link #load} for details)
15214          * @params {Object} return from reader
15215          */
15216         load : true,
15217         /**
15218          * @event loadexception
15219          * Fires if an exception occurs in the Proxy during loading.
15220          * Called with the signature of the Proxy's "loadexception" event.
15221          * If you return Json { data: [] , success: false, .... } then this will be thrown with the following args
15222          * 
15223          * @param {Proxy} 
15224          * @param {Object} return from JsonData.reader() - success, totalRecords, records
15225          * @param {Object} load options 
15226          * @param {Object} jsonData from your request (normally this contains the Exception)
15227          */
15228         loadexception : true
15229     });
15230     
15231     if(this.proxy){
15232         this.proxy = Roo.factory(this.proxy, Roo.data);
15233         this.proxy.xmodule = this.xmodule || false;
15234         this.relayEvents(this.proxy,  ["loadexception"]);
15235     }
15236     this.sortToggle = {};
15237     this.sortOrder = []; // array of order of sorting - updated by grid if multisort is enabled.
15238
15239     Roo.data.Store.superclass.constructor.call(this);
15240
15241     if(this.inlineData){
15242         this.loadData(this.inlineData);
15243         delete this.inlineData;
15244     }
15245 };
15246
15247 Roo.extend(Roo.data.Store, Roo.util.Observable, {
15248      /**
15249     * @cfg {boolean} isLocal   flag if data is locally available (and can be always looked up
15250     * without a remote query - used by combo/forms at present.
15251     */
15252     
15253     /**
15254     * @cfg {Roo.data.DataProxy} proxy [required] The Proxy object which provides access to a data object.
15255     */
15256     /**
15257     * @cfg {Array} data Inline data to be loaded when the store is initialized.
15258     */
15259     /**
15260     * @cfg {Roo.data.DataReader} reader [required]  The Reader object which processes the data object and returns
15261     * an Array of Roo.data.record objects which are cached keyed by their <em>id</em> property.
15262     */
15263     /**
15264     * @cfg {Object} baseParams An object containing properties which are to be sent as parameters
15265     * on any HTTP request
15266     */
15267     /**
15268     * @cfg {Object} sortInfo A config object in the format: {field: "fieldName", direction: "ASC|DESC"}
15269     */
15270     /**
15271     * @cfg {Boolean} multiSort enable multi column sorting (sort is based on the order of columns, remote only at present)
15272     */
15273     multiSort: false,
15274     /**
15275     * @cfg {boolean} remoteSort True if sorting is to be handled by requesting the Proxy to provide a refreshed
15276     * version of the data object in sorted order, as opposed to sorting the Record cache in place (defaults to false).
15277     */
15278     remoteSort : false,
15279
15280     /**
15281     * @cfg {boolean} pruneModifiedRecords True to clear all modified record information each time the store is
15282      * loaded or when a record is removed. (defaults to false).
15283     */
15284     pruneModifiedRecords : false,
15285
15286     // private
15287     lastOptions : null,
15288
15289     /**
15290      * Add Records to the Store and fires the add event.
15291      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
15292      */
15293     add : function(records){
15294         records = [].concat(records);
15295         for(var i = 0, len = records.length; i < len; i++){
15296             records[i].join(this);
15297         }
15298         var index = this.data.length;
15299         this.data.addAll(records);
15300         this.fireEvent("add", this, records, index);
15301     },
15302
15303     /**
15304      * Remove a Record from the Store and fires the remove event.
15305      * @param {Ext.data.Record} record The Roo.data.Record object to remove from the cache.
15306      */
15307     remove : function(record){
15308         var index = this.data.indexOf(record);
15309         this.data.removeAt(index);
15310  
15311         if(this.pruneModifiedRecords){
15312             this.modified.remove(record);
15313         }
15314         this.fireEvent("remove", this, record, index);
15315     },
15316
15317     /**
15318      * Remove all Records from the Store and fires the clear event.
15319      */
15320     removeAll : function(){
15321         this.data.clear();
15322         if(this.pruneModifiedRecords){
15323             this.modified = [];
15324         }
15325         this.fireEvent("clear", this);
15326     },
15327
15328     /**
15329      * Inserts Records to the Store at the given index and fires the add event.
15330      * @param {Number} index The start index at which to insert the passed Records.
15331      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
15332      */
15333     insert : function(index, records){
15334         records = [].concat(records);
15335         for(var i = 0, len = records.length; i < len; i++){
15336             this.data.insert(index, records[i]);
15337             records[i].join(this);
15338         }
15339         this.fireEvent("add", this, records, index);
15340     },
15341
15342     /**
15343      * Get the index within the cache of the passed Record.
15344      * @param {Roo.data.Record} record The Roo.data.Record object to to find.
15345      * @return {Number} The index of the passed Record. Returns -1 if not found.
15346      */
15347     indexOf : function(record){
15348         return this.data.indexOf(record);
15349     },
15350
15351     /**
15352      * Get the index within the cache of the Record with the passed id.
15353      * @param {String} id The id of the Record to find.
15354      * @return {Number} The index of the Record. Returns -1 if not found.
15355      */
15356     indexOfId : function(id){
15357         return this.data.indexOfKey(id);
15358     },
15359
15360     /**
15361      * Get the Record with the specified id.
15362      * @param {String} id The id of the Record to find.
15363      * @return {Roo.data.Record} The Record with the passed id. Returns undefined if not found.
15364      */
15365     getById : function(id){
15366         return this.data.key(id);
15367     },
15368
15369     /**
15370      * Get the Record at the specified index.
15371      * @param {Number} index The index of the Record to find.
15372      * @return {Roo.data.Record} The Record at the passed index. Returns undefined if not found.
15373      */
15374     getAt : function(index){
15375         return this.data.itemAt(index);
15376     },
15377
15378     /**
15379      * Returns a range of Records between specified indices.
15380      * @param {Number} startIndex (optional) The starting index (defaults to 0)
15381      * @param {Number} endIndex (optional) The ending index (defaults to the last Record in the Store)
15382      * @return {Roo.data.Record[]} An array of Records
15383      */
15384     getRange : function(start, end){
15385         return this.data.getRange(start, end);
15386     },
15387
15388     // private
15389     storeOptions : function(o){
15390         o = Roo.apply({}, o);
15391         delete o.callback;
15392         delete o.scope;
15393         this.lastOptions = o;
15394     },
15395
15396     /**
15397      * Loads the Record cache from the configured Proxy using the configured Reader.
15398      * <p>
15399      * If using remote paging, then the first load call must specify the <em>start</em>
15400      * and <em>limit</em> properties in the options.params property to establish the initial
15401      * position within the dataset, and the number of Records to cache on each read from the Proxy.
15402      * <p>
15403      * <strong>It is important to note that for remote data sources, loading is asynchronous,
15404      * and this call will return before the new data has been loaded. Perform any post-processing
15405      * in a callback function, or in a "load" event handler.</strong>
15406      * <p>
15407      * @param {Object} options An object containing properties which control loading options:<ul>
15408      * <li>params {Object} An object containing properties to pass as HTTP parameters to a remote data source.</li>
15409      * <li>params.data {Object} if you are using a MemoryProxy / JsonReader, use this as the data to load stuff..
15410      * <pre>
15411                 {
15412                     data : data,  // array of key=>value data like JsonReader
15413                     total : data.length,
15414                     success : true
15415                     
15416                 }
15417         </pre>
15418             }.</li>
15419      * <li>callback {Function} A function to be called after the Records have been loaded. The callback is
15420      * passed the following arguments:<ul>
15421      * <li>r : Roo.data.Record[]</li>
15422      * <li>options: Options object from the load call</li>
15423      * <li>success: Boolean success indicator</li></ul></li>
15424      * <li>scope {Object} Scope with which to call the callback (defaults to the Store object)</li>
15425      * <li>add {Boolean} indicator to append loaded records rather than replace the current cache.</li>
15426      * </ul>
15427      */
15428     load : function(options){
15429         options = options || {};
15430         if(this.fireEvent("beforeload", this, options) !== false){
15431             this.storeOptions(options);
15432             var p = Roo.apply(options.params || {}, this.baseParams);
15433             // if meta was not loaded from remote source.. try requesting it.
15434             if (!this.reader.metaFromRemote) {
15435                 p._requestMeta = 1;
15436             }
15437             if(this.sortInfo && this.remoteSort){
15438                 var pn = this.paramNames;
15439                 p[pn["sort"]] = this.sortInfo.field;
15440                 p[pn["dir"]] = this.sortInfo.direction;
15441             }
15442             if (this.multiSort) {
15443                 var pn = this.paramNames;
15444                 p[pn["multisort"]] = Roo.encode( { sort : this.sortToggle, order: this.sortOrder });
15445             }
15446             
15447             this.proxy.load(p, this.reader, this.loadRecords, this, options);
15448         }
15449     },
15450
15451     /**
15452      * Reloads the Record cache from the configured Proxy using the configured Reader and
15453      * the options from the last load operation performed.
15454      * @param {Object} options (optional) An object containing properties which may override the options
15455      * used in the last load operation. See {@link #load} for details (defaults to null, in which case
15456      * the most recently used options are reused).
15457      */
15458     reload : function(options){
15459         this.load(Roo.applyIf(options||{}, this.lastOptions));
15460     },
15461
15462     // private
15463     // Called as a callback by the Reader during a load operation.
15464     loadRecords : function(o, options, success){
15465          
15466         if(!o){
15467             if(success !== false){
15468                 this.fireEvent("load", this, [], options, o);
15469             }
15470             if(options.callback){
15471                 options.callback.call(options.scope || this, [], options, false);
15472             }
15473             return;
15474         }
15475         // if data returned failure - throw an exception.
15476         if (o.success === false) {
15477             // show a message if no listener is registered.
15478             if (!this.hasListener('loadexception') && typeof(o.raw.errorMsg) != 'undefined') {
15479                     Roo.MessageBox.alert("Error loading",o.raw.errorMsg);
15480             }
15481             // loadmask wil be hooked into this..
15482             this.fireEvent("loadexception", this, o, options, o.raw.errorMsg);
15483             return;
15484         }
15485         var r = o.records, t = o.totalRecords || r.length;
15486         
15487         this.fireEvent("beforeloadadd", this, r, options, o);
15488         
15489         if(!options || options.add !== true){
15490             if(this.pruneModifiedRecords){
15491                 this.modified = [];
15492             }
15493             for(var i = 0, len = r.length; i < len; i++){
15494                 r[i].join(this);
15495             }
15496             if(this.snapshot){
15497                 this.data = this.snapshot;
15498                 delete this.snapshot;
15499             }
15500             this.data.clear();
15501             this.data.addAll(r);
15502             this.totalLength = t;
15503             this.applySort();
15504             this.fireEvent("datachanged", this);
15505         }else{
15506             this.totalLength = Math.max(t, this.data.length+r.length);
15507             this.add(r);
15508         }
15509         
15510         if(this.parent && !Roo.isIOS && !this.useNativeIOS && this.parent.emptyTitle.length) {
15511                 
15512             var e = new Roo.data.Record({});
15513
15514             e.set(this.parent.displayField, this.parent.emptyTitle);
15515             e.set(this.parent.valueField, '');
15516
15517             this.insert(0, e);
15518         }
15519             
15520         this.fireEvent("load", this, r, options, o);
15521         if(options.callback){
15522             options.callback.call(options.scope || this, r, options, true);
15523         }
15524     },
15525
15526
15527     /**
15528      * Loads data from a passed data block. A Reader which understands the format of the data
15529      * must have been configured in the constructor.
15530      * @param {Object} data The data block from which to read the Records.  The format of the data expected
15531      * is dependent on the type of Reader that is configured and should correspond to that Reader's readRecords parameter.
15532      * @param {Boolean} append (Optional) True to append the new Records rather than replace the existing cache.
15533      */
15534     loadData : function(o, append){
15535         var r = this.reader.readRecords(o);
15536         this.loadRecords(r, {add: append}, true);
15537     },
15538     
15539      /**
15540      * using 'cn' the nested child reader read the child array into it's child stores.
15541      * @param {Object} rec The record with a 'children array
15542      */
15543     loadDataFromChildren : function(rec)
15544     {
15545         this.loadData(this.reader.toLoadData(rec));
15546     },
15547     
15548
15549     /**
15550      * Gets the number of cached records.
15551      * <p>
15552      * <em>If using paging, this may not be the total size of the dataset. If the data object
15553      * used by the Reader contains the dataset size, then the getTotalCount() function returns
15554      * the data set size</em>
15555      */
15556     getCount : function(){
15557         return this.data.length || 0;
15558     },
15559
15560     /**
15561      * Gets the total number of records in the dataset as returned by the server.
15562      * <p>
15563      * <em>If using paging, for this to be accurate, the data object used by the Reader must contain
15564      * the dataset size</em>
15565      */
15566     getTotalCount : function(){
15567         return this.totalLength || 0;
15568     },
15569
15570     /**
15571      * Returns the sort state of the Store as an object with two properties:
15572      * <pre><code>
15573  field {String} The name of the field by which the Records are sorted
15574  direction {String} The sort order, "ASC" or "DESC"
15575      * </code></pre>
15576      */
15577     getSortState : function(){
15578         return this.sortInfo;
15579     },
15580
15581     // private
15582     applySort : function(){
15583         if(this.sortInfo && !this.remoteSort){
15584             var s = this.sortInfo, f = s.field;
15585             var st = this.fields.get(f).sortType;
15586             var fn = function(r1, r2){
15587                 var v1 = st(r1.data[f]), v2 = st(r2.data[f]);
15588                 return v1 > v2 ? 1 : (v1 < v2 ? -1 : 0);
15589             };
15590             this.data.sort(s.direction, fn);
15591             if(this.snapshot && this.snapshot != this.data){
15592                 this.snapshot.sort(s.direction, fn);
15593             }
15594         }
15595     },
15596
15597     /**
15598      * Sets the default sort column and order to be used by the next load operation.
15599      * @param {String} fieldName The name of the field to sort by.
15600      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
15601      */
15602     setDefaultSort : function(field, dir){
15603         this.sortInfo = {field: field, direction: dir ? dir.toUpperCase() : "ASC"};
15604     },
15605
15606     /**
15607      * Sort the Records.
15608      * If remote sorting is used, the sort is performed on the server, and the cache is
15609      * reloaded. If local sorting is used, the cache is sorted internally.
15610      * @param {String} fieldName The name of the field to sort by.
15611      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
15612      */
15613     sort : function(fieldName, dir){
15614         var f = this.fields.get(fieldName);
15615         if(!dir){
15616             this.sortToggle[f.name] = this.sortToggle[f.name] || f.sortDir;
15617             
15618             if(this.multiSort || (this.sortInfo && this.sortInfo.field == f.name) ){ // toggle sort dir
15619                 dir = (this.sortToggle[f.name] || "ASC").toggle("ASC", "DESC");
15620             }else{
15621                 dir = f.sortDir;
15622             }
15623         }
15624         this.sortToggle[f.name] = dir;
15625         this.sortInfo = {field: f.name, direction: dir};
15626         if(!this.remoteSort){
15627             this.applySort();
15628             this.fireEvent("datachanged", this);
15629         }else{
15630             this.load(this.lastOptions);
15631         }
15632     },
15633
15634     /**
15635      * Calls the specified function for each of the Records in the cache.
15636      * @param {Function} fn The function to call. The Record is passed as the first parameter.
15637      * Returning <em>false</em> aborts and exits the iteration.
15638      * @param {Object} scope (optional) The scope in which to call the function (defaults to the Record).
15639      */
15640     each : function(fn, scope){
15641         this.data.each(fn, scope);
15642     },
15643
15644     /**
15645      * Gets all records modified since the last commit.  Modified records are persisted across load operations
15646      * (e.g., during paging).
15647      * @return {Roo.data.Record[]} An array of Records containing outstanding modifications.
15648      */
15649     getModifiedRecords : function(){
15650         return this.modified;
15651     },
15652
15653     // private
15654     createFilterFn : function(property, value, anyMatch){
15655         if(!value.exec){ // not a regex
15656             value = String(value);
15657             if(value.length == 0){
15658                 return false;
15659             }
15660             value = new RegExp((anyMatch === true ? '' : '^') + Roo.escapeRe(value), "i");
15661         }
15662         return function(r){
15663             return value.test(r.data[property]);
15664         };
15665     },
15666
15667     /**
15668      * Sums the value of <i>property</i> for each record between start and end and returns the result.
15669      * @param {String} property A field on your records
15670      * @param {Number} start The record index to start at (defaults to 0)
15671      * @param {Number} end The last record index to include (defaults to length - 1)
15672      * @return {Number} The sum
15673      */
15674     sum : function(property, start, end){
15675         var rs = this.data.items, v = 0;
15676         start = start || 0;
15677         end = (end || end === 0) ? end : rs.length-1;
15678
15679         for(var i = start; i <= end; i++){
15680             v += (rs[i].data[property] || 0);
15681         }
15682         return v;
15683     },
15684
15685     /**
15686      * Filter the records by a specified property.
15687      * @param {String} field A field on your records
15688      * @param {String/RegExp} value Either a string that the field
15689      * should start with or a RegExp to test against the field
15690      * @param {Boolean} anyMatch True to match any part not just the beginning
15691      */
15692     filter : function(property, value, anyMatch){
15693         var fn = this.createFilterFn(property, value, anyMatch);
15694         return fn ? this.filterBy(fn) : this.clearFilter();
15695     },
15696
15697     /**
15698      * Filter by a function. The specified function will be called with each
15699      * record in this data source. If the function returns true the record is included,
15700      * otherwise it is filtered.
15701      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
15702      * @param {Object} scope (optional) The scope of the function (defaults to this)
15703      */
15704     filterBy : function(fn, scope){
15705         this.snapshot = this.snapshot || this.data;
15706         this.data = this.queryBy(fn, scope||this);
15707         this.fireEvent("datachanged", this);
15708     },
15709
15710     /**
15711      * Query the records by a specified property.
15712      * @param {String} field A field on your records
15713      * @param {String/RegExp} value Either a string that the field
15714      * should start with or a RegExp to test against the field
15715      * @param {Boolean} anyMatch True to match any part not just the beginning
15716      * @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
15717      */
15718     query : function(property, value, anyMatch){
15719         var fn = this.createFilterFn(property, value, anyMatch);
15720         return fn ? this.queryBy(fn) : this.data.clone();
15721     },
15722
15723     /**
15724      * Query by a function. The specified function will be called with each
15725      * record in this data source. If the function returns true the record is included
15726      * in the results.
15727      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
15728      * @param {Object} scope (optional) The scope of the function (defaults to this)
15729       @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
15730      **/
15731     queryBy : function(fn, scope){
15732         var data = this.snapshot || this.data;
15733         return data.filterBy(fn, scope||this);
15734     },
15735
15736     /**
15737      * Collects unique values for a particular dataIndex from this store.
15738      * @param {String} dataIndex The property to collect
15739      * @param {Boolean} allowNull (optional) Pass true to allow null, undefined or empty string values
15740      * @param {Boolean} bypassFilter (optional) Pass true to collect from all records, even ones which are filtered
15741      * @return {Array} An array of the unique values
15742      **/
15743     collect : function(dataIndex, allowNull, bypassFilter){
15744         var d = (bypassFilter === true && this.snapshot) ?
15745                 this.snapshot.items : this.data.items;
15746         var v, sv, r = [], l = {};
15747         for(var i = 0, len = d.length; i < len; i++){
15748             v = d[i].data[dataIndex];
15749             sv = String(v);
15750             if((allowNull || !Roo.isEmpty(v)) && !l[sv]){
15751                 l[sv] = true;
15752                 r[r.length] = v;
15753             }
15754         }
15755         return r;
15756     },
15757
15758     /**
15759      * Revert to a view of the Record cache with no filtering applied.
15760      * @param {Boolean} suppressEvent If true the filter is cleared silently without notifying listeners
15761      */
15762     clearFilter : function(suppressEvent){
15763         if(this.snapshot && this.snapshot != this.data){
15764             this.data = this.snapshot;
15765             delete this.snapshot;
15766             if(suppressEvent !== true){
15767                 this.fireEvent("datachanged", this);
15768             }
15769         }
15770     },
15771
15772     // private
15773     afterEdit : function(record){
15774         if(this.modified.indexOf(record) == -1){
15775             this.modified.push(record);
15776         }
15777         this.fireEvent("update", this, record, Roo.data.Record.EDIT);
15778     },
15779     
15780     // private
15781     afterReject : function(record){
15782         this.modified.remove(record);
15783         this.fireEvent("update", this, record, Roo.data.Record.REJECT);
15784     },
15785
15786     // private
15787     afterCommit : function(record){
15788         this.modified.remove(record);
15789         this.fireEvent("update", this, record, Roo.data.Record.COMMIT);
15790     },
15791
15792     /**
15793      * Commit all Records with outstanding changes. To handle updates for changes, subscribe to the
15794      * Store's "update" event, and perform updating when the third parameter is Roo.data.Record.COMMIT.
15795      */
15796     commitChanges : function(){
15797         var m = this.modified.slice(0);
15798         this.modified = [];
15799         for(var i = 0, len = m.length; i < len; i++){
15800             m[i].commit();
15801         }
15802     },
15803
15804     /**
15805      * Cancel outstanding changes on all changed records.
15806      */
15807     rejectChanges : function(){
15808         var m = this.modified.slice(0);
15809         this.modified = [];
15810         for(var i = 0, len = m.length; i < len; i++){
15811             m[i].reject();
15812         }
15813     },
15814
15815     onMetaChange : function(meta, rtype, o){
15816         this.recordType = rtype;
15817         this.fields = rtype.prototype.fields;
15818         delete this.snapshot;
15819         this.sortInfo = meta.sortInfo || this.sortInfo;
15820         this.modified = [];
15821         this.fireEvent('metachange', this, this.reader.meta);
15822     },
15823     
15824     moveIndex : function(data, type)
15825     {
15826         var index = this.indexOf(data);
15827         
15828         var newIndex = index + type;
15829         
15830         this.remove(data);
15831         
15832         this.insert(newIndex, data);
15833         
15834     }
15835 });/*
15836  * Based on:
15837  * Ext JS Library 1.1.1
15838  * Copyright(c) 2006-2007, Ext JS, LLC.
15839  *
15840  * Originally Released Under LGPL - original licence link has changed is not relivant.
15841  *
15842  * Fork - LGPL
15843  * <script type="text/javascript">
15844  */
15845
15846 /**
15847  * @class Roo.data.SimpleStore
15848  * @extends Roo.data.Store
15849  * Small helper class to make creating Stores from Array data easier.
15850  * @cfg {Number} id The array index of the record id. Leave blank to auto generate ids.
15851  * @cfg {Array} fields An array of field definition objects, or field name strings.
15852  * @cfg {Object} an existing reader (eg. copied from another store)
15853  * @cfg {Array} data The multi-dimensional array of data
15854  * @cfg {Roo.data.DataProxy} proxy [not-required]  
15855  * @cfg {Roo.data.Reader} reader  [not-required] 
15856  * @constructor
15857  * @param {Object} config
15858  */
15859 Roo.data.SimpleStore = function(config)
15860 {
15861     Roo.data.SimpleStore.superclass.constructor.call(this, {
15862         isLocal : true,
15863         reader: typeof(config.reader) != 'undefined' ? config.reader : new Roo.data.ArrayReader({
15864                 id: config.id
15865             },
15866             Roo.data.Record.create(config.fields)
15867         ),
15868         proxy : new Roo.data.MemoryProxy(config.data)
15869     });
15870     this.load();
15871 };
15872 Roo.extend(Roo.data.SimpleStore, Roo.data.Store);/*
15873  * Based on:
15874  * Ext JS Library 1.1.1
15875  * Copyright(c) 2006-2007, Ext JS, LLC.
15876  *
15877  * Originally Released Under LGPL - original licence link has changed is not relivant.
15878  *
15879  * Fork - LGPL
15880  * <script type="text/javascript">
15881  */
15882
15883 /**
15884 /**
15885  * @extends Roo.data.Store
15886  * @class Roo.data.JsonStore
15887  * Small helper class to make creating Stores for JSON data easier. <br/>
15888 <pre><code>
15889 var store = new Roo.data.JsonStore({
15890     url: 'get-images.php',
15891     root: 'images',
15892     fields: ['name', 'url', {name:'size', type: 'float'}, {name:'lastmod', type:'date'}]
15893 });
15894 </code></pre>
15895  * <b>Note: Although they are not listed, this class inherits all of the config options of Store,
15896  * JsonReader and HttpProxy (unless inline data is provided).</b>
15897  * @cfg {Array} fields An array of field definition objects, or field name strings.
15898  * @constructor
15899  * @param {Object} config
15900  */
15901 Roo.data.JsonStore = function(c){
15902     Roo.data.JsonStore.superclass.constructor.call(this, Roo.apply(c, {
15903         proxy: !c.data ? new Roo.data.HttpProxy({url: c.url}) : undefined,
15904         reader: new Roo.data.JsonReader(c, c.fields)
15905     }));
15906 };
15907 Roo.extend(Roo.data.JsonStore, Roo.data.Store);/*
15908  * Based on:
15909  * Ext JS Library 1.1.1
15910  * Copyright(c) 2006-2007, Ext JS, LLC.
15911  *
15912  * Originally Released Under LGPL - original licence link has changed is not relivant.
15913  *
15914  * Fork - LGPL
15915  * <script type="text/javascript">
15916  */
15917
15918  
15919 Roo.data.Field = function(config){
15920     if(typeof config == "string"){
15921         config = {name: config};
15922     }
15923     Roo.apply(this, config);
15924     
15925     if(!this.type){
15926         this.type = "auto";
15927     }
15928     
15929     var st = Roo.data.SortTypes;
15930     // named sortTypes are supported, here we look them up
15931     if(typeof this.sortType == "string"){
15932         this.sortType = st[this.sortType];
15933     }
15934     
15935     // set default sortType for strings and dates
15936     if(!this.sortType){
15937         switch(this.type){
15938             case "string":
15939                 this.sortType = st.asUCString;
15940                 break;
15941             case "date":
15942                 this.sortType = st.asDate;
15943                 break;
15944             default:
15945                 this.sortType = st.none;
15946         }
15947     }
15948
15949     // define once
15950     var stripRe = /[\$,%]/g;
15951
15952     // prebuilt conversion function for this field, instead of
15953     // switching every time we're reading a value
15954     if(!this.convert){
15955         var cv, dateFormat = this.dateFormat;
15956         switch(this.type){
15957             case "":
15958             case "auto":
15959             case undefined:
15960                 cv = function(v){ return v; };
15961                 break;
15962             case "string":
15963                 cv = function(v){ return (v === undefined || v === null) ? '' : String(v); };
15964                 break;
15965             case "int":
15966                 cv = function(v){
15967                     return v !== undefined && v !== null && v !== '' ?
15968                            parseInt(String(v).replace(stripRe, ""), 10) : '';
15969                     };
15970                 break;
15971             case "float":
15972                 cv = function(v){
15973                     return v !== undefined && v !== null && v !== '' ?
15974                            parseFloat(String(v).replace(stripRe, ""), 10) : ''; 
15975                     };
15976                 break;
15977             case "bool":
15978             case "boolean":
15979                 cv = function(v){ return v === true || v === "true" || v == 1; };
15980                 break;
15981             case "date":
15982                 cv = function(v){
15983                     if(!v){
15984                         return '';
15985                     }
15986                     if(v instanceof Date){
15987                         return v;
15988                     }
15989                     if(dateFormat){
15990                         if(dateFormat == "timestamp"){
15991                             return new Date(v*1000);
15992                         }
15993                         return Date.parseDate(v, dateFormat);
15994                     }
15995                     var parsed = Date.parse(v);
15996                     return parsed ? new Date(parsed) : null;
15997                 };
15998              break;
15999             
16000         }
16001         this.convert = cv;
16002     }
16003 };
16004
16005 Roo.data.Field.prototype = {
16006     dateFormat: null,
16007     defaultValue: "",
16008     mapping: null,
16009     sortType : null,
16010     sortDir : "ASC"
16011 };/*
16012  * Based on:
16013  * Ext JS Library 1.1.1
16014  * Copyright(c) 2006-2007, Ext JS, LLC.
16015  *
16016  * Originally Released Under LGPL - original licence link has changed is not relivant.
16017  *
16018  * Fork - LGPL
16019  * <script type="text/javascript">
16020  */
16021  
16022 // Base class for reading structured data from a data source.  This class is intended to be
16023 // extended (see ArrayReader, JsonReader and XmlReader) and should not be created directly.
16024
16025 /**
16026  * @class Roo.data.DataReader
16027  * @abstract
16028  * Base class for reading structured data from a data source.  This class is intended to be
16029  * extended (see {Roo.data.ArrayReader}, {Roo.data.JsonReader} and {Roo.data.XmlReader}) and should not be created directly.
16030  */
16031
16032 Roo.data.DataReader = function(meta, recordType){
16033     
16034     this.meta = meta;
16035     
16036     this.recordType = recordType instanceof Array ? 
16037         Roo.data.Record.create(recordType) : recordType;
16038 };
16039
16040 Roo.data.DataReader.prototype = {
16041     
16042     
16043     readerType : 'Data',
16044      /**
16045      * Create an empty record
16046      * @param {Object} data (optional) - overlay some values
16047      * @return {Roo.data.Record} record created.
16048      */
16049     newRow :  function(d) {
16050         var da =  {};
16051         this.recordType.prototype.fields.each(function(c) {
16052             switch( c.type) {
16053                 case 'int' : da[c.name] = 0; break;
16054                 case 'date' : da[c.name] = new Date(); break;
16055                 case 'float' : da[c.name] = 0.0; break;
16056                 case 'boolean' : da[c.name] = false; break;
16057                 default : da[c.name] = ""; break;
16058             }
16059             
16060         });
16061         return new this.recordType(Roo.apply(da, d));
16062     }
16063     
16064     
16065 };/*
16066  * Based on:
16067  * Ext JS Library 1.1.1
16068  * Copyright(c) 2006-2007, Ext JS, LLC.
16069  *
16070  * Originally Released Under LGPL - original licence link has changed is not relivant.
16071  *
16072  * Fork - LGPL
16073  * <script type="text/javascript">
16074  */
16075
16076 /**
16077  * @class Roo.data.DataProxy
16078  * @extends Roo.util.Observable
16079  * @abstract
16080  * This class is an abstract base class for implementations which provide retrieval of
16081  * unformatted data objects.<br>
16082  * <p>
16083  * DataProxy implementations are usually used in conjunction with an implementation of Roo.data.DataReader
16084  * (of the appropriate type which knows how to parse the data object) to provide a block of
16085  * {@link Roo.data.Records} to an {@link Roo.data.Store}.<br>
16086  * <p>
16087  * Custom implementations must implement the load method as described in
16088  * {@link Roo.data.HttpProxy#load}.
16089  */
16090 Roo.data.DataProxy = function(){
16091     this.addEvents({
16092         /**
16093          * @event beforeload
16094          * Fires before a network request is made to retrieve a data object.
16095          * @param {Object} This DataProxy object.
16096          * @param {Object} params The params parameter to the load function.
16097          */
16098         beforeload : true,
16099         /**
16100          * @event load
16101          * Fires before the load method's callback is called.
16102          * @param {Object} This DataProxy object.
16103          * @param {Object} o The data object.
16104          * @param {Object} arg The callback argument object passed to the load function.
16105          */
16106         load : true,
16107         /**
16108          * @event loadexception
16109          * Fires if an Exception occurs during data retrieval.
16110          * @param {Object} This DataProxy object.
16111          * @param {Object} o The data object.
16112          * @param {Object} arg The callback argument object passed to the load function.
16113          * @param {Object} e The Exception.
16114          */
16115         loadexception : true
16116     });
16117     Roo.data.DataProxy.superclass.constructor.call(this);
16118 };
16119
16120 Roo.extend(Roo.data.DataProxy, Roo.util.Observable);
16121
16122     /**
16123      * @cfg {void} listeners (Not available) Constructor blocks listeners from being set
16124      */
16125 /*
16126  * Based on:
16127  * Ext JS Library 1.1.1
16128  * Copyright(c) 2006-2007, Ext JS, LLC.
16129  *
16130  * Originally Released Under LGPL - original licence link has changed is not relivant.
16131  *
16132  * Fork - LGPL
16133  * <script type="text/javascript">
16134  */
16135 /**
16136  * @class Roo.data.MemoryProxy
16137  * @extends Roo.data.DataProxy
16138  * An implementation of Roo.data.DataProxy that simply passes the data specified in its constructor
16139  * to the Reader when its load method is called.
16140  * @constructor
16141  * @param {Object} config  A config object containing the objects needed for the Store to access data,
16142  */
16143 Roo.data.MemoryProxy = function(config){
16144     var data = config;
16145     if (typeof(config) != 'undefined' && typeof(config.data) != 'undefined') {
16146         data = config.data;
16147     }
16148     Roo.data.MemoryProxy.superclass.constructor.call(this);
16149     this.data = data;
16150 };
16151
16152 Roo.extend(Roo.data.MemoryProxy, Roo.data.DataProxy, {
16153     
16154     /**
16155      *  @cfg {Object} data The data object which the Reader uses to construct a block of Roo.data.Records.
16156      */
16157     /**
16158      * Load data from the requested source (in this case an in-memory
16159      * data object passed to the constructor), read the data object into
16160      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
16161      * process that block using the passed callback.
16162      * @param {Object} params This parameter is not used by the MemoryProxy class.
16163      * @param {Roo.data.DataReader} reader The Reader object which converts the data
16164      * object into a block of Roo.data.Records.
16165      * @param {Function} callback The function into which to pass the block of Roo.data.records.
16166      * The function must be passed <ul>
16167      * <li>The Record block object</li>
16168      * <li>The "arg" argument from the load function</li>
16169      * <li>A boolean success indicator</li>
16170      * </ul>
16171      * @param {Object} scope The scope in which to call the callback
16172      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
16173      */
16174     load : function(params, reader, callback, scope, arg){
16175         params = params || {};
16176         var result;
16177         try {
16178             result = reader.readRecords(params.data ? params.data :this.data);
16179         }catch(e){
16180             this.fireEvent("loadexception", this, arg, null, e);
16181             callback.call(scope, null, arg, false);
16182             return;
16183         }
16184         callback.call(scope, result, arg, true);
16185     },
16186     
16187     // private
16188     update : function(params, records){
16189         
16190     }
16191 });/*
16192  * Based on:
16193  * Ext JS Library 1.1.1
16194  * Copyright(c) 2006-2007, Ext JS, LLC.
16195  *
16196  * Originally Released Under LGPL - original licence link has changed is not relivant.
16197  *
16198  * Fork - LGPL
16199  * <script type="text/javascript">
16200  */
16201 /**
16202  * @class Roo.data.HttpProxy
16203  * @extends Roo.data.DataProxy
16204  * An implementation of {@link Roo.data.DataProxy} that reads a data object from an {@link Roo.data.Connection} object
16205  * configured to reference a certain URL.<br><br>
16206  * <p>
16207  * <em>Note that this class cannot be used to retrieve data from a domain other than the domain
16208  * from which the running page was served.<br><br>
16209  * <p>
16210  * For cross-domain access to remote data, use an {@link Roo.data.ScriptTagProxy}.</em><br><br>
16211  * <p>
16212  * Be aware that to enable the browser to parse an XML document, the server must set
16213  * the Content-Type header in the HTTP response to "text/xml".
16214  * @constructor
16215  * @param {Object} conn Connection config options to add to each request (e.g. {url: 'foo.php'} or
16216  * an {@link Roo.data.Connection} object.  If a Connection config is passed, the singleton {@link Roo.Ajax} object
16217  * will be used to make the request.
16218  */
16219 Roo.data.HttpProxy = function(conn){
16220     Roo.data.HttpProxy.superclass.constructor.call(this);
16221     // is conn a conn config or a real conn?
16222     this.conn = conn;
16223     this.useAjax = !conn || !conn.events;
16224   
16225 };
16226
16227 Roo.extend(Roo.data.HttpProxy, Roo.data.DataProxy, {
16228     // thse are take from connection...
16229     
16230     /**
16231      * @cfg {String} url (Optional) The default URL to be used for requests to the server. (defaults to undefined)
16232      */
16233     /**
16234      * @cfg {Object} extraParams (Optional) An object containing properties which are used as
16235      * extra parameters to each request made by this object. (defaults to undefined)
16236      */
16237     /**
16238      * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
16239      *  to each request made by this object. (defaults to undefined)
16240      */
16241     /**
16242      * @cfg {String} method (Optional) The default HTTP method to be used for requests. (defaults to undefined; if not set but parms are present will use POST, otherwise GET)
16243      */
16244     /**
16245      * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
16246      */
16247      /**
16248      * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
16249      * @type Boolean
16250      */
16251   
16252
16253     /**
16254      * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
16255      * @type Boolean
16256      */
16257     /**
16258      * Return the {@link Roo.data.Connection} object being used by this Proxy.
16259      * @return {Connection} The Connection object. This object may be used to subscribe to events on
16260      * a finer-grained basis than the DataProxy events.
16261      */
16262     getConnection : function(){
16263         return this.useAjax ? Roo.Ajax : this.conn;
16264     },
16265
16266     /**
16267      * Load data from the configured {@link Roo.data.Connection}, read the data object into
16268      * a block of Roo.data.Records using the passed {@link Roo.data.DataReader} implementation, and
16269      * process that block using the passed callback.
16270      * @param {Object} params An object containing properties which are to be used as HTTP parameters
16271      * for the request to the remote server.
16272      * @param {Roo.data.DataReader} reader The Reader object which converts the data
16273      * object into a block of Roo.data.Records.
16274      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
16275      * The function must be passed <ul>
16276      * <li>The Record block object</li>
16277      * <li>The "arg" argument from the load function</li>
16278      * <li>A boolean success indicator</li>
16279      * </ul>
16280      * @param {Object} scope The scope in which to call the callback
16281      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
16282      */
16283     load : function(params, reader, callback, scope, arg){
16284         if(this.fireEvent("beforeload", this, params) !== false){
16285             var  o = {
16286                 params : params || {},
16287                 request: {
16288                     callback : callback,
16289                     scope : scope,
16290                     arg : arg
16291                 },
16292                 reader: reader,
16293                 callback : this.loadResponse,
16294                 scope: this
16295             };
16296             if(this.useAjax){
16297                 Roo.applyIf(o, this.conn);
16298                 if(this.activeRequest){
16299                     Roo.Ajax.abort(this.activeRequest);
16300                 }
16301                 this.activeRequest = Roo.Ajax.request(o);
16302             }else{
16303                 this.conn.request(o);
16304             }
16305         }else{
16306             callback.call(scope||this, null, arg, false);
16307         }
16308     },
16309
16310     // private
16311     loadResponse : function(o, success, response){
16312         delete this.activeRequest;
16313         if(!success){
16314             this.fireEvent("loadexception", this, o, response);
16315             o.request.callback.call(o.request.scope, null, o.request.arg, false);
16316             return;
16317         }
16318         var result;
16319         try {
16320             result = o.reader.read(response);
16321         }catch(e){
16322             o.success = false;
16323             o.raw = { errorMsg : response.responseText };
16324             this.fireEvent("loadexception", this, o, response, e);
16325             o.request.callback.call(o.request.scope, o, o.request.arg, false);
16326             return;
16327         }
16328         
16329         this.fireEvent("load", this, o, o.request.arg);
16330         o.request.callback.call(o.request.scope, result, o.request.arg, true);
16331     },
16332
16333     // private
16334     update : function(dataSet){
16335
16336     },
16337
16338     // private
16339     updateResponse : function(dataSet){
16340
16341     }
16342 });/*
16343  * Based on:
16344  * Ext JS Library 1.1.1
16345  * Copyright(c) 2006-2007, Ext JS, LLC.
16346  *
16347  * Originally Released Under LGPL - original licence link has changed is not relivant.
16348  *
16349  * Fork - LGPL
16350  * <script type="text/javascript">
16351  */
16352
16353 /**
16354  * @class Roo.data.ScriptTagProxy
16355  * An implementation of Roo.data.DataProxy that reads a data object from a URL which may be in a domain
16356  * other than the originating domain of the running page.<br><br>
16357  * <p>
16358  * <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
16359  * of the running page, you must use this class, rather than DataProxy.</em><br><br>
16360  * <p>
16361  * The content passed back from a server resource requested by a ScriptTagProxy is executable JavaScript
16362  * source code that is used as the source inside a &lt;script> tag.<br><br>
16363  * <p>
16364  * In order for the browser to process the returned data, the server must wrap the data object
16365  * with a call to a callback function, the name of which is passed as a parameter by the ScriptTagProxy.
16366  * Below is a Java example for a servlet which returns data for either a ScriptTagProxy, or an HttpProxy
16367  * depending on whether the callback name was passed:
16368  * <p>
16369  * <pre><code>
16370 boolean scriptTag = false;
16371 String cb = request.getParameter("callback");
16372 if (cb != null) {
16373     scriptTag = true;
16374     response.setContentType("text/javascript");
16375 } else {
16376     response.setContentType("application/x-json");
16377 }
16378 Writer out = response.getWriter();
16379 if (scriptTag) {
16380     out.write(cb + "(");
16381 }
16382 out.print(dataBlock.toJsonString());
16383 if (scriptTag) {
16384     out.write(");");
16385 }
16386 </pre></code>
16387  *
16388  * @constructor
16389  * @param {Object} config A configuration object.
16390  */
16391 Roo.data.ScriptTagProxy = function(config){
16392     Roo.data.ScriptTagProxy.superclass.constructor.call(this);
16393     Roo.apply(this, config);
16394     this.head = document.getElementsByTagName("head")[0];
16395 };
16396
16397 Roo.data.ScriptTagProxy.TRANS_ID = 1000;
16398
16399 Roo.extend(Roo.data.ScriptTagProxy, Roo.data.DataProxy, {
16400     /**
16401      * @cfg {String} url The URL from which to request the data object.
16402      */
16403     /**
16404      * @cfg {Number} timeout (Optional) The number of milliseconds to wait for a response. Defaults to 30 seconds.
16405      */
16406     timeout : 30000,
16407     /**
16408      * @cfg {String} callbackParam (Optional) The name of the parameter to pass to the server which tells
16409      * the server the name of the callback function set up by the load call to process the returned data object.
16410      * Defaults to "callback".<p>The server-side processing must read this parameter value, and generate
16411      * javascript output which calls this named function passing the data object as its only parameter.
16412      */
16413     callbackParam : "callback",
16414     /**
16415      *  @cfg {Boolean} nocache (Optional) Defaults to true. Disable cacheing by adding a unique parameter
16416      * name to the request.
16417      */
16418     nocache : true,
16419
16420     /**
16421      * Load data from the configured URL, read the data object into
16422      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
16423      * process that block using the passed callback.
16424      * @param {Object} params An object containing properties which are to be used as HTTP parameters
16425      * for the request to the remote server.
16426      * @param {Roo.data.DataReader} reader The Reader object which converts the data
16427      * object into a block of Roo.data.Records.
16428      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
16429      * The function must be passed <ul>
16430      * <li>The Record block object</li>
16431      * <li>The "arg" argument from the load function</li>
16432      * <li>A boolean success indicator</li>
16433      * </ul>
16434      * @param {Object} scope The scope in which to call the callback
16435      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
16436      */
16437     load : function(params, reader, callback, scope, arg){
16438         if(this.fireEvent("beforeload", this, params) !== false){
16439
16440             var p = Roo.urlEncode(Roo.apply(params, this.extraParams));
16441
16442             var url = this.url;
16443             url += (url.indexOf("?") != -1 ? "&" : "?") + p;
16444             if(this.nocache){
16445                 url += "&_dc=" + (new Date().getTime());
16446             }
16447             var transId = ++Roo.data.ScriptTagProxy.TRANS_ID;
16448             var trans = {
16449                 id : transId,
16450                 cb : "stcCallback"+transId,
16451                 scriptId : "stcScript"+transId,
16452                 params : params,
16453                 arg : arg,
16454                 url : url,
16455                 callback : callback,
16456                 scope : scope,
16457                 reader : reader
16458             };
16459             var conn = this;
16460
16461             window[trans.cb] = function(o){
16462                 conn.handleResponse(o, trans);
16463             };
16464
16465             url += String.format("&{0}={1}", this.callbackParam, trans.cb);
16466
16467             if(this.autoAbort !== false){
16468                 this.abort();
16469             }
16470
16471             trans.timeoutId = this.handleFailure.defer(this.timeout, this, [trans]);
16472
16473             var script = document.createElement("script");
16474             script.setAttribute("src", url);
16475             script.setAttribute("type", "text/javascript");
16476             script.setAttribute("id", trans.scriptId);
16477             this.head.appendChild(script);
16478
16479             this.trans = trans;
16480         }else{
16481             callback.call(scope||this, null, arg, false);
16482         }
16483     },
16484
16485     // private
16486     isLoading : function(){
16487         return this.trans ? true : false;
16488     },
16489
16490     /**
16491      * Abort the current server request.
16492      */
16493     abort : function(){
16494         if(this.isLoading()){
16495             this.destroyTrans(this.trans);
16496         }
16497     },
16498
16499     // private
16500     destroyTrans : function(trans, isLoaded){
16501         this.head.removeChild(document.getElementById(trans.scriptId));
16502         clearTimeout(trans.timeoutId);
16503         if(isLoaded){
16504             window[trans.cb] = undefined;
16505             try{
16506                 delete window[trans.cb];
16507             }catch(e){}
16508         }else{
16509             // if hasn't been loaded, wait for load to remove it to prevent script error
16510             window[trans.cb] = function(){
16511                 window[trans.cb] = undefined;
16512                 try{
16513                     delete window[trans.cb];
16514                 }catch(e){}
16515             };
16516         }
16517     },
16518
16519     // private
16520     handleResponse : function(o, trans){
16521         this.trans = false;
16522         this.destroyTrans(trans, true);
16523         var result;
16524         try {
16525             result = trans.reader.readRecords(o);
16526         }catch(e){
16527             this.fireEvent("loadexception", this, o, trans.arg, e);
16528             trans.callback.call(trans.scope||window, null, trans.arg, false);
16529             return;
16530         }
16531         this.fireEvent("load", this, o, trans.arg);
16532         trans.callback.call(trans.scope||window, result, trans.arg, true);
16533     },
16534
16535     // private
16536     handleFailure : function(trans){
16537         this.trans = false;
16538         this.destroyTrans(trans, false);
16539         this.fireEvent("loadexception", this, null, trans.arg);
16540         trans.callback.call(trans.scope||window, null, trans.arg, false);
16541     }
16542 });/*
16543  * Based on:
16544  * Ext JS Library 1.1.1
16545  * Copyright(c) 2006-2007, Ext JS, LLC.
16546  *
16547  * Originally Released Under LGPL - original licence link has changed is not relivant.
16548  *
16549  * Fork - LGPL
16550  * <script type="text/javascript">
16551  */
16552
16553 /**
16554  * @class Roo.data.JsonReader
16555  * @extends Roo.data.DataReader
16556  * Data reader class to create an Array of Roo.data.Record objects from a JSON response
16557  * based on mappings in a provided Roo.data.Record constructor.
16558  * 
16559  * The default behaviour of a store is to send ?_requestMeta=1, unless the class has recieved 'metaData' property
16560  * in the reply previously. 
16561  * 
16562  * <p>
16563  * Example code:
16564  * <pre><code>
16565 var RecordDef = Roo.data.Record.create([
16566     {name: 'name', mapping: 'name'},     // "mapping" property not needed if it's the same as "name"
16567     {name: 'occupation'}                 // This field will use "occupation" as the mapping.
16568 ]);
16569 var myReader = new Roo.data.JsonReader({
16570     totalProperty: "results",    // The property which contains the total dataset size (optional)
16571     root: "rows",                // The property which contains an Array of row objects
16572     id: "id"                     // The property within each row object that provides an ID for the record (optional)
16573 }, RecordDef);
16574 </code></pre>
16575  * <p>
16576  * This would consume a JSON file like this:
16577  * <pre><code>
16578 { 'results': 2, 'rows': [
16579     { 'id': 1, 'name': 'Bill', occupation: 'Gardener' },
16580     { 'id': 2, 'name': 'Ben', occupation: 'Horticulturalist' } ]
16581 }
16582 </code></pre>
16583  * @cfg {String} totalProperty Name of the property from which to retrieve the total number of records
16584  * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
16585  * paged from the remote server.
16586  * @cfg {String} successProperty Name of the property from which to retrieve the success attribute used by forms.
16587  * @cfg {String} root name of the property which contains the Array of row objects.
16588  * @cfg {String} id Name of the property within a row object that contains a record identifier value.
16589  * @cfg {Array} fields Array of field definition objects
16590  * @constructor
16591  * Create a new JsonReader
16592  * @param {Object} meta Metadata configuration options
16593  * @param {Object} recordType Either an Array of field definition objects,
16594  * or an {@link Roo.data.Record} object created using {@link Roo.data.Record#create}.
16595  */
16596 Roo.data.JsonReader = function(meta, recordType){
16597     
16598     meta = meta || {};
16599     // set some defaults:
16600     Roo.applyIf(meta, {
16601         totalProperty: 'total',
16602         successProperty : 'success',
16603         root : 'data',
16604         id : 'id'
16605     });
16606     
16607     Roo.data.JsonReader.superclass.constructor.call(this, meta, recordType||meta.fields);
16608 };
16609 Roo.extend(Roo.data.JsonReader, Roo.data.DataReader, {
16610     
16611     readerType : 'Json',
16612     
16613     /**
16614      * @prop {Boolean} metaFromRemote  - if the meta data was loaded from the remote source.
16615      * Used by Store query builder to append _requestMeta to params.
16616      * 
16617      */
16618     metaFromRemote : false,
16619     /**
16620      * This method is only used by a DataProxy which has retrieved data from a remote server.
16621      * @param {Object} response The XHR object which contains the JSON data in its responseText.
16622      * @return {Object} data A data block which is used by an Roo.data.Store object as
16623      * a cache of Roo.data.Records.
16624      */
16625     read : function(response){
16626         var json = response.responseText;
16627        
16628         var o = /* eval:var:o */ eval("("+json+")");
16629         if(!o) {
16630             throw {message: "JsonReader.read: Json object not found"};
16631         }
16632         
16633         if(o.metaData){
16634             
16635             delete this.ef;
16636             this.metaFromRemote = true;
16637             this.meta = o.metaData;
16638             this.recordType = Roo.data.Record.create(o.metaData.fields);
16639             this.onMetaChange(this.meta, this.recordType, o);
16640         }
16641         return this.readRecords(o);
16642     },
16643
16644     // private function a store will implement
16645     onMetaChange : function(meta, recordType, o){
16646
16647     },
16648
16649     /**
16650          * @ignore
16651          */
16652     simpleAccess: function(obj, subsc) {
16653         return obj[subsc];
16654     },
16655
16656         /**
16657          * @ignore
16658          */
16659     getJsonAccessor: function(){
16660         var re = /[\[\.]/;
16661         return function(expr) {
16662             try {
16663                 return(re.test(expr))
16664                     ? new Function("obj", "return obj." + expr)
16665                     : function(obj){
16666                         return obj[expr];
16667                     };
16668             } catch(e){}
16669             return Roo.emptyFn;
16670         };
16671     }(),
16672
16673     /**
16674      * Create a data block containing Roo.data.Records from an XML document.
16675      * @param {Object} o An object which contains an Array of row objects in the property specified
16676      * in the config as 'root, and optionally a property, specified in the config as 'totalProperty'
16677      * which contains the total size of the dataset.
16678      * @return {Object} data A data block which is used by an Roo.data.Store object as
16679      * a cache of Roo.data.Records.
16680      */
16681     readRecords : function(o){
16682         /**
16683          * After any data loads, the raw JSON data is available for further custom processing.
16684          * @type Object
16685          */
16686         this.o = o;
16687         var s = this.meta, Record = this.recordType,
16688             f = Record ? Record.prototype.fields : null, fi = f ? f.items : [], fl = f ? f.length : 0;
16689
16690 //      Generate extraction functions for the totalProperty, the root, the id, and for each field
16691         if (!this.ef) {
16692             if(s.totalProperty) {
16693                     this.getTotal = this.getJsonAccessor(s.totalProperty);
16694                 }
16695                 if(s.successProperty) {
16696                     this.getSuccess = this.getJsonAccessor(s.successProperty);
16697                 }
16698                 this.getRoot = s.root ? this.getJsonAccessor(s.root) : function(p){return p;};
16699                 if (s.id) {
16700                         var g = this.getJsonAccessor(s.id);
16701                         this.getId = function(rec) {
16702                                 var r = g(rec);  
16703                                 return (r === undefined || r === "") ? null : r;
16704                         };
16705                 } else {
16706                         this.getId = function(){return null;};
16707                 }
16708             this.ef = [];
16709             for(var jj = 0; jj < fl; jj++){
16710                 f = fi[jj];
16711                 var map = (f.mapping !== undefined && f.mapping !== null) ? f.mapping : f.name;
16712                 this.ef[jj] = this.getJsonAccessor(map);
16713             }
16714         }
16715
16716         var root = this.getRoot(o), c = root.length, totalRecords = c, success = true;
16717         if(s.totalProperty){
16718             var vt = parseInt(this.getTotal(o), 10);
16719             if(!isNaN(vt)){
16720                 totalRecords = vt;
16721             }
16722         }
16723         if(s.successProperty){
16724             var vs = this.getSuccess(o);
16725             if(vs === false || vs === 'false'){
16726                 success = false;
16727             }
16728         }
16729         var records = [];
16730         for(var i = 0; i < c; i++){
16731             var n = root[i];
16732             var values = {};
16733             var id = this.getId(n);
16734             for(var j = 0; j < fl; j++){
16735                 f = fi[j];
16736                                 var v = this.ef[j](n);
16737                                 if (!f.convert) {
16738                                         Roo.log('missing convert for ' + f.name);
16739                                         Roo.log(f);
16740                                         continue;
16741                                 }
16742                                 values[f.name] = f.convert((v !== undefined) ? v : f.defaultValue);
16743             }
16744                         if (!Record) {
16745                                 return {
16746                                         raw : { errorMsg : "JSON Reader Error: fields or metadata not available to create Record" },
16747                                         success : false,
16748                                         records : [],
16749                                         totalRecords : 0
16750                                 };
16751                         }
16752             var record = new Record(values, id);
16753             record.json = n;
16754             records[i] = record;
16755         }
16756         return {
16757             raw : o,
16758             success : success,
16759             records : records,
16760             totalRecords : totalRecords
16761         };
16762     },
16763     // used when loading children.. @see loadDataFromChildren
16764     toLoadData: function(rec)
16765     {
16766         // expect rec just to be an array.. eg [a,b,c, [...] << cn ]
16767         var data = typeof(rec.data.cn) == 'undefined' ? [] : rec.data.cn;
16768         return { data : data, total : data.length };
16769         
16770     }
16771 });/*
16772  * Based on:
16773  * Ext JS Library 1.1.1
16774  * Copyright(c) 2006-2007, Ext JS, LLC.
16775  *
16776  * Originally Released Under LGPL - original licence link has changed is not relivant.
16777  *
16778  * Fork - LGPL
16779  * <script type="text/javascript">
16780  */
16781
16782 /**
16783  * @class Roo.data.ArrayReader
16784  * @extends Roo.data.DataReader
16785  * Data reader class to create an Array of Roo.data.Record objects from an Array.
16786  * Each element of that Array represents a row of data fields. The
16787  * fields are pulled into a Record object using as a subscript, the <em>mapping</em> property
16788  * of the field definition if it exists, or the field's ordinal position in the definition.<br>
16789  * <p>
16790  * Example code:.
16791  * <pre><code>
16792 var RecordDef = Roo.data.Record.create([
16793     {name: 'name', mapping: 1},         // "mapping" only needed if an "id" field is present which
16794     {name: 'occupation', mapping: 2}    // precludes using the ordinal position as the index.
16795 ]);
16796 var myReader = new Roo.data.ArrayReader({
16797     id: 0                     // The subscript within row Array that provides an ID for the Record (optional)
16798 }, RecordDef);
16799 </code></pre>
16800  * <p>
16801  * This would consume an Array like this:
16802  * <pre><code>
16803 [ [1, 'Bill', 'Gardener'], [2, 'Ben', 'Horticulturalist'] ]
16804   </code></pre>
16805  
16806  * @constructor
16807  * Create a new JsonReader
16808  * @param {Object} meta Metadata configuration options.
16809  * @param {Object|Array} recordType Either an Array of field definition objects
16810  * 
16811  * @cfg {Array} fields Array of field definition objects
16812  * @cfg {String} id Name of the property within a row object that contains a record identifier value.
16813  * as specified to {@link Roo.data.Record#create},
16814  * or an {@link Roo.data.Record} object
16815  *
16816  * 
16817  * created using {@link Roo.data.Record#create}.
16818  */
16819 Roo.data.ArrayReader = function(meta, recordType)
16820 {    
16821     Roo.data.ArrayReader.superclass.constructor.call(this, meta, recordType||meta.fields);
16822 };
16823
16824 Roo.extend(Roo.data.ArrayReader, Roo.data.JsonReader, {
16825     
16826       /**
16827      * Create a data block containing Roo.data.Records from an XML document.
16828      * @param {Object} o An Array of row objects which represents the dataset.
16829      * @return {Object} A data block which is used by an {@link Roo.data.Store} object as
16830      * a cache of Roo.data.Records.
16831      */
16832     readRecords : function(o)
16833     {
16834         var sid = this.meta ? this.meta.id : null;
16835         var recordType = this.recordType, fields = recordType.prototype.fields;
16836         var records = [];
16837         var root = o;
16838         for(var i = 0; i < root.length; i++){
16839             var n = root[i];
16840             var values = {};
16841             var id = ((sid || sid === 0) && n[sid] !== undefined && n[sid] !== "" ? n[sid] : null);
16842             for(var j = 0, jlen = fields.length; j < jlen; j++){
16843                 var f = fields.items[j];
16844                 var k = f.mapping !== undefined && f.mapping !== null ? f.mapping : j;
16845                 var v = n[k] !== undefined ? n[k] : f.defaultValue;
16846                 v = f.convert(v);
16847                 values[f.name] = v;
16848             }
16849             var record = new recordType(values, id);
16850             record.json = n;
16851             records[records.length] = record;
16852         }
16853         return {
16854             records : records,
16855             totalRecords : records.length
16856         };
16857     },
16858     // used when loading children.. @see loadDataFromChildren
16859     toLoadData: function(rec)
16860     {
16861         // expect rec just to be an array.. eg [a,b,c, [...] << cn ]
16862         return typeof(rec.data.cn) == 'undefined' ? [] : rec.data.cn;
16863         
16864     }
16865     
16866     
16867 });/*
16868  * - LGPL
16869  * * 
16870  */
16871
16872 /**
16873  * @class Roo.bootstrap.form.ComboBox
16874  * @extends Roo.bootstrap.form.TriggerField
16875  * A combobox control with support for autocomplete, remote-loading, paging and many other features.
16876  * @cfg {Boolean} append (true|false) default false
16877  * @cfg {Boolean} autoFocus (true|false) auto focus the first item, default true
16878  * @cfg {Boolean} tickable ComboBox with tickable selections (true|false), default false
16879  * @cfg {Boolean} triggerList trigger show the list or not (true|false) default true
16880  * @cfg {Boolean} showToggleBtn show toggle button or not (true|false) default true
16881  * @cfg {String} btnPosition set the position of the trigger button (left | right) default right
16882  * @cfg {Boolean} animate default true
16883  * @cfg {Boolean} emptyResultText only for touch device
16884  * @cfg {String} triggerText multiple combobox trigger button text default 'Select'
16885  * @cfg {String} emptyTitle default ''
16886  * @cfg {Number} width fixed with? experimental
16887  * @constructor
16888  * Create a new ComboBox.
16889  * @param {Object} config Configuration options
16890  */
16891 Roo.bootstrap.form.ComboBox = function(config){
16892     Roo.bootstrap.form.ComboBox.superclass.constructor.call(this, config);
16893     this.addEvents({
16894         /**
16895          * @event expand
16896          * Fires when the dropdown list is expanded
16897         * @param {Roo.bootstrap.form.ComboBox} combo This combo box
16898         */
16899         'expand' : true,
16900         /**
16901          * @event collapse
16902          * Fires when the dropdown list is collapsed
16903         * @param {Roo.bootstrap.form.ComboBox} combo This combo box
16904         */
16905         'collapse' : true,
16906         /**
16907          * @event beforeselect
16908          * Fires before a list item is selected. Return false to cancel the selection.
16909         * @param {Roo.bootstrap.form.ComboBox} combo This combo box
16910         * @param {Roo.data.Record} record The data record returned from the underlying store
16911         * @param {Number} index The index of the selected item in the dropdown list
16912         */
16913         'beforeselect' : true,
16914         /**
16915          * @event select
16916          * Fires when a list item is selected
16917         * @param {Roo.bootstrap.form.ComboBox} combo This combo box
16918         * @param {Roo.data.Record} record The data record returned from the underlying store (or false on clear)
16919         * @param {Number} index The index of the selected item in the dropdown list
16920         */
16921         'select' : true,
16922         /**
16923          * @event beforequery
16924          * Fires before all queries are processed. Return false to cancel the query or set cancel to true.
16925          * The event object passed has these properties:
16926         * @param {Roo.bootstrap.form.ComboBox} combo This combo box
16927         * @param {String} query The query
16928         * @param {Boolean} forceAll true to force "all" query
16929         * @param {Boolean} cancel true to cancel the query
16930         * @param {Object} e The query event object
16931         */
16932         'beforequery': true,
16933          /**
16934          * @event add
16935          * Fires when the 'add' icon is pressed (add a listener to enable add button)
16936         * @param {Roo.bootstrap.form.ComboBox} combo This combo box
16937         */
16938         'add' : true,
16939         /**
16940          * @event edit
16941          * Fires when the 'edit' icon is pressed (add a listener to enable add button)
16942         * @param {Roo.bootstrap.form.ComboBox} combo This combo box
16943         * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
16944         */
16945         'edit' : true,
16946         /**
16947          * @event remove
16948          * Fires when the remove value from the combobox array
16949         * @param {Roo.bootstrap.form.ComboBox} combo This combo box
16950         */
16951         'remove' : true,
16952         /**
16953          * @event afterremove
16954          * Fires when the remove value from the combobox array
16955         * @param {Roo.bootstrap.form.ComboBox} combo This combo box
16956         */
16957         'afterremove' : true,
16958         /**
16959          * @event specialfilter
16960          * Fires when specialfilter
16961             * @param {Roo.bootstrap.form.ComboBox} combo This combo box
16962             */
16963         'specialfilter' : true,
16964         /**
16965          * @event tick
16966          * Fires when tick the element
16967             * @param {Roo.bootstrap.form.ComboBox} combo This combo box
16968             */
16969         'tick' : true,
16970         /**
16971          * @event touchviewdisplay
16972          * Fires when touch view require special display (default is using displayField)
16973             * @param {Roo.bootstrap.form.ComboBox} combo This combo box
16974             * @param {Object} cfg set html .
16975             */
16976         'touchviewdisplay' : true
16977         
16978     });
16979     
16980     this.item = [];
16981     this.tickItems = [];
16982     
16983     this.selectedIndex = -1;
16984     if(this.mode == 'local'){
16985         if(config.queryDelay === undefined){
16986             this.queryDelay = 10;
16987         }
16988         if(config.minChars === undefined){
16989             this.minChars = 0;
16990         }
16991     }
16992 };
16993
16994 Roo.extend(Roo.bootstrap.form.ComboBox, Roo.bootstrap.form.TriggerField, {
16995      
16996     /**
16997      * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
16998      * rendering into an Roo.Editor, defaults to false)
16999      */
17000     /**
17001      * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
17002      * {tag: "input", type: "text", size: "24", autocomplete: "off"})
17003      */
17004     /**
17005      * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
17006      */
17007     /**
17008      * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
17009      * the dropdown list (defaults to undefined, with no header element)
17010      */
17011
17012      /**
17013      * @cfg {String/Roo.Template} tpl The template to use to render the output default is  '<a class="dropdown-item" href="#">{' + this.displayField + '}</a>' 
17014      */
17015      
17016      /**
17017      * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
17018      */
17019     listWidth: undefined,
17020     /**
17021      * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
17022      * mode = 'remote' or 'text' if mode = 'local')
17023      */
17024     displayField: undefined,
17025     
17026     /**
17027      * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
17028      * mode = 'remote' or 'value' if mode = 'local'). 
17029      * Note: use of a valueField requires the user make a selection
17030      * in order for a value to be mapped.
17031      */
17032     valueField: undefined,
17033     /**
17034      * @cfg {String} modalTitle The title of the dialog that pops up on mobile views.
17035      */
17036     modalTitle : '',
17037     
17038     /**
17039      * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
17040      * field's data value (defaults to the underlying DOM element's name)
17041      */
17042     hiddenName: undefined,
17043     /**
17044      * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
17045      */
17046     listClass: '',
17047     /**
17048      * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
17049      */
17050     selectedClass: 'active',
17051     
17052     /**
17053      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
17054      */
17055     shadow:'sides',
17056     /**
17057      * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
17058      * anchor positions (defaults to 'tl-bl')
17059      */
17060     listAlign: 'tl-bl?',
17061     /**
17062      * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
17063      */
17064     maxHeight: 300,
17065     /**
17066      * @cfg {String} triggerAction The action to execute when the trigger field is activated.  Use 'all' to run the
17067      * query specified by the allQuery config option (defaults to 'query')
17068      */
17069     triggerAction: 'query',
17070     /**
17071      * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
17072      * (defaults to 4, does not apply if editable = false)
17073      */
17074     minChars : 4,
17075     /**
17076      * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
17077      * delay (typeAheadDelay) if it matches a known value (defaults to false)
17078      */
17079     typeAhead: false,
17080     /**
17081      * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
17082      * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
17083      */
17084     queryDelay: 500,
17085     /**
17086      * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
17087      * filter queries will execute with page start and limit parameters.  Only applies when mode = 'remote' (defaults to 0)
17088      */
17089     pageSize: 0,
17090     /**
17091      * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus.  Only applies
17092      * when editable = true (defaults to false)
17093      */
17094     selectOnFocus:false,
17095     /**
17096      * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
17097      */
17098     queryParam: 'query',
17099     /**
17100      * @cfg {String} loadingText The text to display in the dropdown list while data is loading.  Only applies
17101      * when mode = 'remote' (defaults to 'Loading...')
17102      */
17103     loadingText: 'Loading...',
17104     /**
17105      * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
17106      */
17107     resizable: false,
17108     /**
17109      * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
17110      */
17111     handleHeight : 8,
17112     /**
17113      * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
17114      * traditional select (defaults to true)
17115      */
17116     editable: true,
17117     /**
17118      * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
17119      */
17120     allQuery: '',
17121     /**
17122      * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
17123      */
17124     mode: 'remote',
17125     /**
17126      * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
17127      * listWidth has a higher value)
17128      */
17129     minListWidth : 70,
17130     /**
17131      * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
17132      * allow the user to set arbitrary text into the field (defaults to false)
17133      */
17134     forceSelection:false,
17135     /**
17136      * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
17137      * if typeAhead = true (defaults to 250)
17138      */
17139     typeAheadDelay : 250,
17140     /**
17141      * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
17142      * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
17143      */
17144     valueNotFoundText : undefined,
17145     /**
17146      * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
17147      */
17148     blockFocus : false,
17149     
17150     /**
17151      * @cfg {Boolean} disableClear Disable showing of clear button.
17152      */
17153     disableClear : false,
17154     /**
17155      * @cfg {Boolean} alwaysQuery  Disable caching of results, and always send query
17156      */
17157     alwaysQuery : false,
17158     
17159     /**
17160      * @cfg {Boolean} multiple  (true|false) ComboBobArray, default false
17161      */
17162     multiple : false,
17163     
17164     /**
17165      * @cfg {String} invalidClass DEPRICATED - uses BS4 is-valid now
17166      */
17167     invalidClass : "has-warning",
17168     
17169     /**
17170      * @cfg {String} validClass DEPRICATED - uses BS4 is-valid now
17171      */
17172     validClass : "has-success",
17173     
17174     /**
17175      * @cfg {Boolean} specialFilter (true|false) special filter default false
17176      */
17177     specialFilter : false,
17178     
17179     /**
17180      * @cfg {Boolean} mobileTouchView (true|false) show mobile touch view when using a mobile default true
17181      */
17182     mobileTouchView : true,
17183     
17184     /**
17185      * @cfg {Boolean} useNativeIOS (true|false) render it as classic select for ios, not support dynamic load data (default false)
17186      */
17187     useNativeIOS : false,
17188     
17189     /**
17190      * @cfg {Boolean} mobile_restrict_height (true|false) restrict height for touch view
17191      */
17192     mobile_restrict_height : false,
17193     
17194     ios_options : false,
17195     
17196     //private
17197     addicon : false,
17198     editicon: false,
17199     
17200     page: 0,
17201     hasQuery: false,
17202     append: false,
17203     loadNext: false,
17204     autoFocus : true,
17205     tickable : false,
17206     btnPosition : 'right',
17207     triggerList : true,
17208     showToggleBtn : true,
17209     animate : true,
17210     emptyResultText: 'Empty',
17211     triggerText : 'Select',
17212     emptyTitle : '',
17213     width : false,
17214     
17215     // element that contains real text value.. (when hidden is used..)
17216     
17217     getAutoCreate : function()
17218     {   
17219         var cfg = false;
17220         //render
17221         /*
17222          * Render classic select for iso
17223          */
17224         
17225         if(Roo.isIOS && this.useNativeIOS){
17226             cfg = this.getAutoCreateNativeIOS();
17227             return cfg;
17228         }
17229         
17230         /*
17231          * Touch Devices
17232          */
17233         
17234         if(Roo.isTouch && this.mobileTouchView){
17235             cfg = this.getAutoCreateTouchView();
17236             return cfg;;
17237         }
17238         
17239         /*
17240          *  Normal ComboBox
17241          */
17242         if(!this.tickable){
17243             cfg = Roo.bootstrap.form.ComboBox.superclass.getAutoCreate.call(this);
17244             return cfg;
17245         }
17246         
17247         /*
17248          *  ComboBox with tickable selections
17249          */
17250              
17251         var align = this.labelAlign || this.parentLabelAlign();
17252         
17253         cfg = {
17254             cls : 'form-group roo-combobox-tickable' //input-group
17255         };
17256         
17257         var btn_text_select = '';
17258         var btn_text_done = '';
17259         var btn_text_cancel = '';
17260         
17261         if (this.btn_text_show) {
17262             btn_text_select = 'Select';
17263             btn_text_done = 'Done';
17264             btn_text_cancel = 'Cancel'; 
17265         }
17266         
17267         var buttons = {
17268             tag : 'div',
17269             cls : 'tickable-buttons',
17270             cn : [
17271                 {
17272                     tag : 'button',
17273                     type : 'button',
17274                     cls : 'btn btn-link btn-edit pull-' + this.btnPosition,
17275                     //html : this.triggerText
17276                     html: btn_text_select
17277                 },
17278                 {
17279                     tag : 'button',
17280                     type : 'button',
17281                     name : 'ok',
17282                     cls : 'btn btn-link btn-ok pull-' + this.btnPosition,
17283                     //html : 'Done'
17284                     html: btn_text_done
17285                 },
17286                 {
17287                     tag : 'button',
17288                     type : 'button',
17289                     name : 'cancel',
17290                     cls : 'btn btn-link btn-cancel pull-' + this.btnPosition,
17291                     //html : 'Cancel'
17292                     html: btn_text_cancel
17293                 }
17294             ]
17295         };
17296         
17297         if(this.editable){
17298             buttons.cn.unshift({
17299                 tag: 'input',
17300                 cls: 'roo-select2-search-field-input'
17301             });
17302         }
17303         
17304         var _this = this;
17305         
17306         Roo.each(buttons.cn, function(c){
17307             if (_this.size) {
17308                 c.cls += ' btn-' + _this.size;
17309             }
17310
17311             if (_this.disabled) {
17312                 c.disabled = true;
17313             }
17314         });
17315         
17316         var box = {
17317             tag: 'div',
17318             style : 'display: contents',
17319             cn: [
17320                 {
17321                     tag: 'input',
17322                     type : 'hidden',
17323                     cls: 'form-hidden-field'
17324                 },
17325                 {
17326                     tag: 'ul',
17327                     cls: 'roo-select2-choices',
17328                     cn:[
17329                         {
17330                             tag: 'li',
17331                             cls: 'roo-select2-search-field',
17332                             cn: [
17333                                 buttons
17334                             ]
17335                         }
17336                     ]
17337                 }
17338             ]
17339         };
17340         
17341         var combobox = {
17342             cls: 'roo-select2-container input-group roo-select2-container-multi',
17343             cn: [
17344                 
17345                 box
17346 //                {
17347 //                    tag: 'ul',
17348 //                    cls: 'typeahead typeahead-long dropdown-menu',
17349 //                    style: 'display:none; max-height:' + this.maxHeight + 'px;'
17350 //                }
17351             ]
17352         };
17353         
17354         if(this.hasFeedback && !this.allowBlank){
17355             
17356             var feedback = {
17357                 tag: 'span',
17358                 cls: 'glyphicon form-control-feedback'
17359             };
17360
17361             combobox.cn.push(feedback);
17362         }
17363         
17364         
17365         
17366         var indicator = {
17367             tag : 'i',
17368             cls : 'roo-required-indicator ' + (this.indicatorpos == 'right'  ? 'right' : 'left') +'-indicator text-danger fa fa-lg fa-star',
17369             tooltip : 'This field is required'
17370         };
17371         if (Roo.bootstrap.version == 4) {
17372             indicator = {
17373                 tag : 'i',
17374                 style : 'display:none'
17375             };
17376         }
17377         if (align ==='left' && this.fieldLabel.length) {
17378             
17379             cfg.cls += ' roo-form-group-label-left'  + (Roo.bootstrap.version == 4 ? ' row' : '');
17380             
17381             cfg.cn = [
17382                 indicator,
17383                 {
17384                     tag: 'label',
17385                     'for' :  id,
17386                     cls : 'control-label col-form-label',
17387                     html : this.fieldLabel
17388
17389                 },
17390                 {
17391                     cls : "", 
17392                     cn: [
17393                         combobox
17394                     ]
17395                 }
17396
17397             ];
17398             
17399             var labelCfg = cfg.cn[1];
17400             var contentCfg = cfg.cn[2];
17401             
17402
17403             if(this.indicatorpos == 'right'){
17404                 
17405                 cfg.cn = [
17406                     {
17407                         tag: 'label',
17408                         'for' :  id,
17409                         cls : 'control-label col-form-label',
17410                         cn : [
17411                             {
17412                                 tag : 'span',
17413                                 html : this.fieldLabel
17414                             },
17415                             indicator
17416                         ]
17417                     },
17418                     {
17419                         cls : "",
17420                         cn: [
17421                             combobox
17422                         ]
17423                     }
17424
17425                 ];
17426                 
17427                 
17428                 
17429                 labelCfg = cfg.cn[0];
17430                 contentCfg = cfg.cn[1];
17431             
17432             }
17433             
17434             if(this.labelWidth > 12){
17435                 labelCfg.style = "width: " + this.labelWidth + 'px';
17436             }
17437             if(this.width * 1 > 0){
17438                 contentCfg.style = "width: " + this.width + 'px';
17439             }
17440             if(this.labelWidth < 13 && this.labelmd == 0){
17441                 this.labelmd = this.labelWidth;
17442             }
17443             
17444             if(this.labellg > 0){
17445                 labelCfg.cls += ' col-lg-' + this.labellg;
17446                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
17447             }
17448             
17449             if(this.labelmd > 0){
17450                 labelCfg.cls += ' col-md-' + this.labelmd;
17451                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
17452             }
17453             
17454             if(this.labelsm > 0){
17455                 labelCfg.cls += ' col-sm-' + this.labelsm;
17456                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
17457             }
17458             
17459             if(this.labelxs > 0){
17460                 labelCfg.cls += ' col-xs-' + this.labelxs;
17461                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
17462             }
17463                 
17464                 
17465         } else if ( this.fieldLabel.length) {
17466 //                Roo.log(" label");
17467                  cfg.cn = [
17468                    indicator,
17469                     {
17470                         tag: 'label',
17471                         //cls : 'input-group-addon',
17472                         html : this.fieldLabel
17473                     },
17474                     combobox
17475                 ];
17476                 
17477                 if(this.indicatorpos == 'right'){
17478                     cfg.cn = [
17479                         {
17480                             tag: 'label',
17481                             //cls : 'input-group-addon',
17482                             html : this.fieldLabel
17483                         },
17484                         indicator,
17485                         combobox
17486                     ];
17487                     
17488                 }
17489
17490         } else {
17491             
17492 //                Roo.log(" no label && no align");
17493                 cfg = combobox
17494                      
17495                 
17496         }
17497          
17498         var settings=this;
17499         ['xs','sm','md','lg'].map(function(size){
17500             if (settings[size]) {
17501                 cfg.cls += ' col-' + size + '-' + settings[size];
17502             }
17503         });
17504         
17505         return cfg;
17506         
17507     },
17508     
17509     _initEventsCalled : false,
17510     
17511     // private
17512     initEvents: function()
17513     {   
17514         if (this._initEventsCalled) { // as we call render... prevent looping...
17515             return;
17516         }
17517         this._initEventsCalled = true;
17518         
17519         if (!this.store) {
17520             throw "can not find store for combo";
17521         }
17522         
17523         this.indicator = this.indicatorEl();
17524         
17525         this.store = Roo.factory(this.store, Roo.data);
17526         this.store.parent = this;
17527         
17528         // if we are building from html. then this element is so complex, that we can not really
17529         // use the rendered HTML.
17530         // so we have to trash and replace the previous code.
17531         if (Roo.XComponent.build_from_html) {
17532             // remove this element....
17533             var e = this.el.dom, k=0;
17534             while (e ) { e = e.previousSibling;  ++k;}
17535
17536             this.el.remove();
17537             
17538             this.el=false;
17539             this.rendered = false;
17540             
17541             this.render(this.parent().getChildContainer(true), k);
17542         }
17543         
17544         if(Roo.isIOS && this.useNativeIOS){
17545             this.initIOSView();
17546             return;
17547         }
17548         
17549         /*
17550          * Touch Devices
17551          */
17552         
17553         if(Roo.isTouch && this.mobileTouchView){
17554             this.initTouchView();
17555             return;
17556         }
17557         
17558         if(this.tickable){
17559             this.initTickableEvents();
17560             return;
17561         }
17562         
17563         Roo.bootstrap.form.ComboBox.superclass.initEvents.call(this);
17564         
17565         if(this.hiddenName){
17566             
17567             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
17568             
17569             this.hiddenField.dom.value =
17570                 this.hiddenValue !== undefined ? this.hiddenValue :
17571                 this.value !== undefined ? this.value : '';
17572
17573             // prevent input submission
17574             this.el.dom.removeAttribute('name');
17575             this.hiddenField.dom.setAttribute('name', this.hiddenName);
17576              
17577              
17578         }
17579         //if(Roo.isGecko){
17580         //    this.el.dom.setAttribute('autocomplete', 'off');
17581         //}
17582         
17583         var cls = 'x-combo-list';
17584         
17585         //this.list = new Roo.Layer({
17586         //    shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false
17587         //});
17588         
17589         var _this = this;
17590         
17591         (function(){
17592             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
17593             _this.list.setWidth(lw);
17594         }).defer(100);
17595         
17596         this.list.on('mouseover', this.onViewOver, this);
17597         this.list.on('mousemove', this.onViewMove, this);
17598         this.list.on('scroll', this.onViewScroll, this);
17599         
17600         /*
17601         this.list.swallowEvent('mousewheel');
17602         this.assetHeight = 0;
17603
17604         if(this.title){
17605             this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
17606             this.assetHeight += this.header.getHeight();
17607         }
17608
17609         this.innerList = this.list.createChild({cls:cls+'-inner'});
17610         this.innerList.on('mouseover', this.onViewOver, this);
17611         this.innerList.on('mousemove', this.onViewMove, this);
17612         this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
17613         
17614         if(this.allowBlank && !this.pageSize && !this.disableClear){
17615             this.footer = this.list.createChild({cls:cls+'-ft'});
17616             this.pageTb = new Roo.Toolbar(this.footer);
17617            
17618         }
17619         if(this.pageSize){
17620             this.footer = this.list.createChild({cls:cls+'-ft'});
17621             this.pageTb = new Roo.PagingToolbar(this.footer, this.store,
17622                     {pageSize: this.pageSize});
17623             
17624         }
17625         
17626         if (this.pageTb && this.allowBlank && !this.disableClear) {
17627             var _this = this;
17628             this.pageTb.add(new Roo.Toolbar.Fill(), {
17629                 cls: 'x-btn-icon x-btn-clear',
17630                 text: '&#160;',
17631                 handler: function()
17632                 {
17633                     _this.collapse();
17634                     _this.clearValue();
17635                     _this.onSelect(false, -1);
17636                 }
17637             });
17638         }
17639         if (this.footer) {
17640             this.assetHeight += this.footer.getHeight();
17641         }
17642         */
17643             
17644         if(!this.tpl){
17645             this.tpl = Roo.bootstrap.version == 4 ?
17646                 '<a class="dropdown-item" href="#">{' + this.displayField + '}</a>' :  // 4 does not need <li> and it get's really confisued.
17647                 '<li><a class="dropdown-item" href="#">{' + this.displayField + '}</a></li>';
17648         }
17649
17650         this.view = new Roo.View(this.list, this.tpl, {
17651             singleSelect:true, store: this.store, selectedClass: this.selectedClass
17652         });
17653         //this.view.wrapEl.setDisplayed(false);
17654         this.view.on('click', this.onViewClick, this);
17655         
17656         
17657         this.store.on('beforeload', this.onBeforeLoad, this);
17658         this.store.on('load', this.onLoad, this);
17659         this.store.on('loadexception', this.onLoadException, this);
17660         /*
17661         if(this.resizable){
17662             this.resizer = new Roo.Resizable(this.list,  {
17663                pinned:true, handles:'se'
17664             });
17665             this.resizer.on('resize', function(r, w, h){
17666                 this.maxHeight = h-this.handleHeight-this.list.getFrameWidth('tb')-this.assetHeight;
17667                 this.listWidth = w;
17668                 this.innerList.setWidth(w - this.list.getFrameWidth('lr'));
17669                 this.restrictHeight();
17670             }, this);
17671             this[this.pageSize?'footer':'innerList'].setStyle('margin-bottom', this.handleHeight+'px');
17672         }
17673         */
17674         if(!this.editable){
17675             this.editable = true;
17676             this.setEditable(false);
17677         }
17678         
17679         /*
17680         
17681         if (typeof(this.events.add.listeners) != 'undefined') {
17682             
17683             this.addicon = this.wrap.createChild(
17684                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-add' });  
17685        
17686             this.addicon.on('click', function(e) {
17687                 this.fireEvent('add', this);
17688             }, this);
17689         }
17690         if (typeof(this.events.edit.listeners) != 'undefined') {
17691             
17692             this.editicon = this.wrap.createChild(
17693                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-edit' });  
17694             if (this.addicon) {
17695                 this.editicon.setStyle('margin-left', '40px');
17696             }
17697             this.editicon.on('click', function(e) {
17698                 
17699                 // we fire even  if inothing is selected..
17700                 this.fireEvent('edit', this, this.lastData );
17701                 
17702             }, this);
17703         }
17704         */
17705         
17706         this.keyNav = new Roo.KeyNav(this.inputEl(), {
17707             "up" : function(e){
17708                 this.inKeyMode = true;
17709                 this.selectPrev();
17710             },
17711
17712             "down" : function(e){
17713                 if(!this.isExpanded()){
17714                     this.onTriggerClick();
17715                 }else{
17716                     this.inKeyMode = true;
17717                     this.selectNext();
17718                 }
17719             },
17720
17721             "enter" : function(e){
17722 //                this.onViewClick();
17723                 //return true;
17724                 this.collapse();
17725                 
17726                 if(this.fireEvent("specialkey", this, e)){
17727                     this.onViewClick(false);
17728                 }
17729                 
17730                 return true;
17731             },
17732
17733             "esc" : function(e){
17734                 this.collapse();
17735             },
17736
17737             "tab" : function(e){
17738                 this.collapse();
17739                 
17740                 if(this.fireEvent("specialkey", this, e)){
17741                     this.onViewClick(false);
17742                 }
17743                 
17744                 return true;
17745             },
17746
17747             scope : this,
17748
17749             doRelay : function(foo, bar, hname){
17750                 if(hname == 'down' || this.scope.isExpanded()){
17751                    return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
17752                 }
17753                 return true;
17754             },
17755
17756             forceKeyDown: true
17757         });
17758         
17759         
17760         this.queryDelay = Math.max(this.queryDelay || 10,
17761                 this.mode == 'local' ? 10 : 250);
17762         
17763         
17764         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
17765         
17766         if(this.typeAhead){
17767             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
17768         }
17769         if(this.editable !== false){
17770             this.inputEl().on("keyup", this.onKeyUp, this);
17771         }
17772         if(this.forceSelection){
17773             this.inputEl().on('blur', this.doForce, this);
17774         }
17775         
17776         if(this.multiple){
17777             this.choices = this.el.select('ul.roo-select2-choices', true).first();
17778             this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
17779         }
17780     },
17781     
17782     initTickableEvents: function()
17783     {   
17784         this.createList();
17785         
17786         if(this.hiddenName){
17787             
17788             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
17789             
17790             this.hiddenField.dom.value =
17791                 this.hiddenValue !== undefined ? this.hiddenValue :
17792                 this.value !== undefined ? this.value : '';
17793
17794             // prevent input submission
17795             this.el.dom.removeAttribute('name');
17796             this.hiddenField.dom.setAttribute('name', this.hiddenName);
17797              
17798              
17799         }
17800         
17801 //        this.list = this.el.select('ul.dropdown-menu',true).first();
17802         
17803         this.choices = this.el.select('ul.roo-select2-choices', true).first();
17804         this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
17805         if(this.triggerList){
17806             this.searchField.on("click", this.onSearchFieldClick, this, {preventDefault:true});
17807         }
17808          
17809         this.trigger = this.el.select('.tickable-buttons > .btn-edit', true).first();
17810         this.trigger.on("click", this.onTickableTriggerClick, this, {preventDefault:true});
17811         
17812         this.okBtn = this.el.select('.tickable-buttons > .btn-ok', true).first();
17813         this.cancelBtn = this.el.select('.tickable-buttons > .btn-cancel', true).first();
17814         
17815         this.okBtn.on('click', this.onTickableFooterButtonClick, this, this.okBtn);
17816         this.cancelBtn.on('click', this.onTickableFooterButtonClick, this, this.cancelBtn);
17817         
17818         this.trigger.setVisibilityMode(Roo.Element.DISPLAY);
17819         this.okBtn.setVisibilityMode(Roo.Element.DISPLAY);
17820         this.cancelBtn.setVisibilityMode(Roo.Element.DISPLAY);
17821         
17822         this.okBtn.hide();
17823         this.cancelBtn.hide();
17824         
17825         var _this = this;
17826         
17827         (function(){
17828             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
17829             _this.list.setWidth(lw);
17830         }).defer(100);
17831         
17832         this.list.on('mouseover', this.onViewOver, this);
17833         this.list.on('mousemove', this.onViewMove, this);
17834         
17835         this.list.on('scroll', this.onViewScroll, this);
17836         
17837         if(!this.tpl){
17838             this.tpl = '<li class="roo-select2-result"><div class="checkbox"><input id="{roo-id}"' + 
17839                 'type="checkbox" {roo-data-checked}><label for="{roo-id}"><b>{' + this.displayField + '}</b></label></div></li>';
17840         }
17841
17842         this.view = new Roo.View(this.list, this.tpl, {
17843             singleSelect:true,
17844             tickable:true,
17845             parent:this,
17846             store: this.store,
17847             selectedClass: this.selectedClass
17848         });
17849         
17850         //this.view.wrapEl.setDisplayed(false);
17851         this.view.on('click', this.onViewClick, this);
17852         
17853         
17854         
17855         this.store.on('beforeload', this.onBeforeLoad, this);
17856         this.store.on('load', this.onLoad, this);
17857         this.store.on('loadexception', this.onLoadException, this);
17858         
17859         if(this.editable){
17860             this.keyNav = new Roo.KeyNav(this.tickableInputEl(), {
17861                 "up" : function(e){
17862                     this.inKeyMode = true;
17863                     this.selectPrev();
17864                 },
17865
17866                 "down" : function(e){
17867                     this.inKeyMode = true;
17868                     this.selectNext();
17869                 },
17870
17871                 "enter" : function(e){
17872                     if(this.fireEvent("specialkey", this, e)){
17873                         this.onViewClick(false);
17874                     }
17875                     
17876                     return true;
17877                 },
17878
17879                 "esc" : function(e){
17880                     this.onTickableFooterButtonClick(e, false, false);
17881                 },
17882
17883                 "tab" : function(e){
17884                     this.fireEvent("specialkey", this, e);
17885                     
17886                     this.onTickableFooterButtonClick(e, false, false);
17887                     
17888                     return true;
17889                 },
17890
17891                 scope : this,
17892
17893                 doRelay : function(e, fn, key){
17894                     if(this.scope.isExpanded()){
17895                        return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
17896                     }
17897                     return true;
17898                 },
17899
17900                 forceKeyDown: true
17901             });
17902         }
17903         
17904         this.queryDelay = Math.max(this.queryDelay || 10,
17905                 this.mode == 'local' ? 10 : 250);
17906         
17907         
17908         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
17909         
17910         if(this.typeAhead){
17911             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
17912         }
17913         
17914         if(this.editable !== false){
17915             this.tickableInputEl().on("keyup", this.onKeyUp, this);
17916         }
17917         
17918         this.indicator = this.indicatorEl();
17919         
17920         if(this.indicator){
17921             this.indicator.setVisibilityMode(Roo.Element.DISPLAY);
17922             this.indicator.hide();
17923         }
17924         
17925     },
17926
17927     onDestroy : function(){
17928         if(this.view){
17929             this.view.setStore(null);
17930             this.view.el.removeAllListeners();
17931             this.view.el.remove();
17932             this.view.purgeListeners();
17933         }
17934         if(this.list){
17935             this.list.dom.innerHTML  = '';
17936         }
17937         
17938         if(this.store){
17939             this.store.un('beforeload', this.onBeforeLoad, this);
17940             this.store.un('load', this.onLoad, this);
17941             this.store.un('loadexception', this.onLoadException, this);
17942         }
17943         Roo.bootstrap.form.ComboBox.superclass.onDestroy.call(this);
17944     },
17945
17946     // private
17947     fireKey : function(e){
17948         if(e.isNavKeyPress() && !this.list.isVisible()){
17949             this.fireEvent("specialkey", this, e);
17950         }
17951     },
17952
17953     // private
17954     onResize: function(w, h)
17955     {
17956         
17957         
17958 //        Roo.bootstrap.form.ComboBox.superclass.onResize.apply(this, arguments);
17959 //        
17960 //        if(typeof w != 'number'){
17961 //            // we do not handle it!?!?
17962 //            return;
17963 //        }
17964 //        var tw = this.trigger.getWidth();
17965 //       // tw += this.addicon ? this.addicon.getWidth() : 0;
17966 //       // tw += this.editicon ? this.editicon.getWidth() : 0;
17967 //        var x = w - tw;
17968 //        this.inputEl().setWidth( this.adjustWidth('input', x));
17969 //            
17970 //        //this.trigger.setStyle('left', x+'px');
17971 //        
17972 //        if(this.list && this.listWidth === undefined){
17973 //            var lw = Math.max(x + this.trigger.getWidth(), this.minListWidth);
17974 //            this.list.setWidth(lw);
17975 //            this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
17976 //        }
17977         
17978     
17979         
17980     },
17981
17982     /**
17983      * Allow or prevent the user from directly editing the field text.  If false is passed,
17984      * the user will only be able to select from the items defined in the dropdown list.  This method
17985      * is the runtime equivalent of setting the 'editable' config option at config time.
17986      * @param {Boolean} value True to allow the user to directly edit the field text
17987      */
17988     setEditable : function(value){
17989         if(value == this.editable){
17990             return;
17991         }
17992         this.editable = value;
17993         if(!value){
17994             this.inputEl().dom.setAttribute('readOnly', true);
17995             this.inputEl().on('mousedown', this.onTriggerClick,  this);
17996             this.inputEl().addClass('x-combo-noedit');
17997         }else{
17998             this.inputEl().dom.removeAttribute('readOnly');
17999             this.inputEl().un('mousedown', this.onTriggerClick,  this);
18000             this.inputEl().removeClass('x-combo-noedit');
18001         }
18002     },
18003
18004     // private
18005     
18006     onBeforeLoad : function(combo,opts){
18007         if(!this.hasFocus){
18008             return;
18009         }
18010          if (!opts.add) {
18011             this.list.dom.innerHTML = '<li class="loading-indicator">'+(this.loadingText||'loading')+'</li>' ;
18012          }
18013         this.restrictHeight();
18014         this.selectedIndex = -1;
18015     },
18016
18017     // private
18018     onLoad : function(){
18019         
18020         this.hasQuery = false;
18021         
18022         if(!this.hasFocus){
18023             return;
18024         }
18025         
18026         if(typeof(this.loading) !== 'undefined' && this.loading !== null){
18027             this.loading.hide();
18028         }
18029         
18030         if(this.store.getCount() > 0){
18031             
18032             this.expand();
18033             this.restrictHeight();
18034             if(this.lastQuery == this.allQuery){
18035                 if(this.editable && !this.tickable){
18036                     this.inputEl().dom.select();
18037                 }
18038                 
18039                 if(
18040                     !this.selectByValue(this.value, true) &&
18041                     this.autoFocus && 
18042                     (
18043                         !this.store.lastOptions ||
18044                         typeof(this.store.lastOptions.add) == 'undefined' || 
18045                         this.store.lastOptions.add != true
18046                     )
18047                 ){
18048                     this.select(0, true);
18049                 }
18050             }else{
18051                 if(this.autoFocus){
18052                     this.selectNext();
18053                 }
18054                 if(this.typeAhead && this.lastKey != Roo.EventObject.BACKSPACE && this.lastKey != Roo.EventObject.DELETE){
18055                     this.taTask.delay(this.typeAheadDelay);
18056                 }
18057             }
18058         }else{
18059             this.onEmptyResults();
18060         }
18061         
18062         //this.el.focus();
18063     },
18064     // private
18065     onLoadException : function()
18066     {
18067         this.hasQuery = false;
18068         
18069         if(typeof(this.loading) !== 'undefined' && this.loading !== null){
18070             this.loading.hide();
18071         }
18072         
18073         if(this.tickable && this.editable){
18074             return;
18075         }
18076         
18077         this.collapse();
18078         // only causes errors at present
18079         //Roo.log(this.store.reader.jsonData);
18080         //if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
18081             // fixme
18082             //Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
18083         //}
18084         
18085         
18086     },
18087     // private
18088     onTypeAhead : function(){
18089         if(this.store.getCount() > 0){
18090             var r = this.store.getAt(0);
18091             var newValue = r.data[this.displayField];
18092             var len = newValue.length;
18093             var selStart = this.getRawValue().length;
18094             
18095             if(selStart != len){
18096                 this.setRawValue(newValue);
18097                 this.selectText(selStart, newValue.length);
18098             }
18099         }
18100     },
18101
18102     // private
18103     onSelect : function(record, index){
18104         
18105         if(this.fireEvent('beforeselect', this, record, index) !== false){
18106         
18107             this.setFromData(index > -1 ? record.data : false);
18108             
18109             this.collapse();
18110             this.fireEvent('select', this, record, index);
18111         }
18112     },
18113
18114     /**
18115      * Returns the currently selected field value or empty string if no value is set.
18116      * @return {String} value The selected value
18117      */
18118     getValue : function()
18119     {
18120         if(Roo.isIOS && this.useNativeIOS){
18121             return this.ios_options[this.inputEl().dom.selectedIndex].data[this.valueField];
18122         }
18123         
18124         if(this.multiple){
18125             return (this.hiddenField) ? this.hiddenField.dom.value : this.value;
18126         }
18127         
18128         if(this.valueField){
18129             return typeof this.value != 'undefined' ? this.value : '';
18130         }else{
18131             return Roo.bootstrap.form.ComboBox.superclass.getValue.call(this);
18132         }
18133     },
18134     
18135     getRawValue : function()
18136     {
18137         if(Roo.isIOS && this.useNativeIOS){
18138             return this.ios_options[this.inputEl().dom.selectedIndex].data[this.displayField];
18139         }
18140         
18141         var v = this.inputEl().getValue();
18142         
18143         return v;
18144     },
18145
18146     /**
18147      * Clears any text/value currently set in the field
18148      */
18149     clearValue : function(){
18150         
18151         if(this.hiddenField){
18152             this.hiddenField.dom.value = '';
18153         }
18154         this.value = '';
18155         this.setRawValue('');
18156         this.lastSelectionText = '';
18157         this.lastData = false;
18158         
18159         var close = this.closeTriggerEl();
18160         
18161         if(close){
18162             close.hide();
18163         }
18164         
18165         this.validate();
18166         
18167     },
18168
18169     /**
18170      * Sets the specified value into the field.  If the value finds a match, the corresponding record text
18171      * will be displayed in the field.  If the value does not match the data value of an existing item,
18172      * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
18173      * Otherwise the field will be blank (although the value will still be set).
18174      * @param {String} value The value to match
18175      */
18176     setValue : function(v)
18177     {
18178         if(Roo.isIOS && this.useNativeIOS){
18179             this.setIOSValue(v);
18180             return;
18181         }
18182         
18183         if(this.multiple){
18184             this.syncValue();
18185             return;
18186         }
18187         
18188         var text = v;
18189         if(this.valueField){
18190             var r = this.findRecord(this.valueField, v);
18191             if(r){
18192                 text = r.data[this.displayField];
18193             }else if(this.valueNotFoundText !== undefined){
18194                 text = this.valueNotFoundText;
18195             }
18196         }
18197         this.lastSelectionText = text;
18198         if(this.hiddenField){
18199             this.hiddenField.dom.value = v;
18200         }
18201         Roo.bootstrap.form.ComboBox.superclass.setValue.call(this, text);
18202         this.value = v;
18203         
18204         var close = this.closeTriggerEl();
18205         
18206         if(close){
18207             (v && (v.length || v * 1 > 0)) ? close.show() : close.hide();
18208         }
18209         
18210         this.validate();
18211     },
18212     /**
18213      * @property {Object} the last set data for the element
18214      */
18215     
18216     lastData : false,
18217     /**
18218      * Sets the value of the field based on a object which is related to the record format for the store.
18219      * @param {Object} value the value to set as. or false on reset?
18220      */
18221     setFromData : function(o){
18222         
18223         if(this.multiple){
18224             this.addItem(o);
18225             return;
18226         }
18227             
18228         var dv = ''; // display value
18229         var vv = ''; // value value..
18230         this.lastData = o;
18231         if (this.displayField) {
18232             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
18233         } else {
18234             // this is an error condition!!!
18235             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
18236         }
18237         
18238         if(this.valueField){
18239             vv = !o || typeof(o[this.valueField]) == 'undefined' ? dv : o[this.valueField];
18240         }
18241         
18242         var close = this.closeTriggerEl();
18243         
18244         if(close){
18245             if(dv.length || vv * 1 > 0){
18246                 close.show() ;
18247                 this.blockFocus=true;
18248             } else {
18249                 close.hide();
18250             }             
18251         }
18252         
18253         if(this.hiddenField){
18254             this.hiddenField.dom.value = vv;
18255             
18256             this.lastSelectionText = dv;
18257             Roo.bootstrap.form.ComboBox.superclass.setValue.call(this, dv);
18258             this.value = vv;
18259             return;
18260         }
18261         // no hidden field.. - we store the value in 'value', but still display
18262         // display field!!!!
18263         this.lastSelectionText = dv;
18264         Roo.bootstrap.form.ComboBox.superclass.setValue.call(this, dv);
18265         this.value = vv;
18266         
18267         
18268         
18269     },
18270     // private
18271     reset : function(){
18272         // overridden so that last data is reset..
18273         
18274         if(this.multiple){
18275             this.clearItem();
18276             return;
18277         }
18278         
18279         this.setValue(this.originalValue);
18280         //this.clearInvalid();
18281         this.lastData = false;
18282         if (this.view) {
18283             this.view.clearSelections();
18284         }
18285         
18286         this.validate();
18287     },
18288     // private
18289     findRecord : function(prop, value){
18290         var record;
18291         if(this.store.getCount() > 0){
18292             this.store.each(function(r){
18293                 if(r.data[prop] == value){
18294                     record = r;
18295                     return false;
18296                 }
18297                 return true;
18298             });
18299         }
18300         return record;
18301     },
18302     
18303     getName: function()
18304     {
18305         // returns hidden if it's set..
18306         if (!this.rendered) {return ''};
18307         return !this.hiddenName && this.inputEl().dom.name  ? this.inputEl().dom.name : (this.hiddenName || '');
18308         
18309     },
18310     // private
18311     onViewMove : function(e, t){
18312         this.inKeyMode = false;
18313     },
18314
18315     // private
18316     onViewOver : function(e, t){
18317         if(this.inKeyMode){ // prevent key nav and mouse over conflicts
18318             return;
18319         }
18320         var item = this.view.findItemFromChild(t);
18321         
18322         if(item){
18323             var index = this.view.indexOf(item);
18324             this.select(index, false);
18325         }
18326     },
18327
18328     // private
18329     onViewClick : function(view, doFocus, el, e)
18330     {
18331         var index = this.view.getSelectedIndexes()[0];
18332         
18333         var r = this.store.getAt(index);
18334         
18335         if(this.tickable){
18336             
18337             if(typeof(e) != 'undefined' && e.getTarget().nodeName.toLowerCase() != 'input'){
18338                 return;
18339             }
18340             
18341             var rm = false;
18342             var _this = this;
18343             
18344             Roo.each(this.tickItems, function(v,k){
18345                 
18346                 if(typeof(v) != 'undefined' && v[_this.valueField] == r.data[_this.valueField]){
18347                     Roo.log(v);
18348                     _this.tickItems.splice(k, 1);
18349                     
18350                     if(typeof(e) == 'undefined' && view == false){
18351                         Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = false;
18352                     }
18353                     
18354                     rm = true;
18355                     return;
18356                 }
18357             });
18358             
18359             if(rm){
18360                 return;
18361             }
18362             
18363             if(this.fireEvent('tick', this, r, index, Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked) !== false){
18364                 this.tickItems.push(r.data);
18365             }
18366             
18367             if(typeof(e) == 'undefined' && view == false){
18368                 Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = true;
18369             }
18370                     
18371             return;
18372         }
18373         
18374         if(r){
18375             this.onSelect(r, index);
18376         }
18377         if(doFocus !== false && !this.blockFocus){
18378             this.inputEl().focus();
18379         }
18380     },
18381
18382     // private
18383     restrictHeight : function(){
18384         //this.innerList.dom.style.height = '';
18385         //var inner = this.innerList.dom;
18386         //var h = Math.max(inner.clientHeight, inner.offsetHeight, inner.scrollHeight);
18387         //this.innerList.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
18388         //this.list.beginUpdate();
18389         //this.list.setHeight(this.innerList.getHeight()+this.list.getFrameWidth('tb')+(this.resizable?this.handleHeight:0)+this.assetHeight);
18390         this.list.alignTo(this.inputEl(), this.listAlign);
18391         this.list.alignTo(this.inputEl(), this.listAlign);
18392         //this.list.endUpdate();
18393     },
18394
18395     // private
18396     onEmptyResults : function(){
18397         
18398         if(this.tickable && this.editable){
18399             this.hasFocus = false;
18400             this.restrictHeight();
18401             return;
18402         }
18403         
18404         this.collapse();
18405     },
18406
18407     /**
18408      * Returns true if the dropdown list is expanded, else false.
18409      */
18410     isExpanded : function(){
18411         return this.list.isVisible();
18412     },
18413
18414     /**
18415      * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
18416      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
18417      * @param {String} value The data value of the item to select
18418      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
18419      * selected item if it is not currently in view (defaults to true)
18420      * @return {Boolean} True if the value matched an item in the list, else false
18421      */
18422     selectByValue : function(v, scrollIntoView){
18423         if(v !== undefined && v !== null){
18424             var r = this.findRecord(this.valueField || this.displayField, v);
18425             if(r){
18426                 this.select(this.store.indexOf(r), scrollIntoView);
18427                 return true;
18428             }
18429         }
18430         return false;
18431     },
18432
18433     /**
18434      * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
18435      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
18436      * @param {Number} index The zero-based index of the list item to select
18437      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
18438      * selected item if it is not currently in view (defaults to true)
18439      */
18440     select : function(index, scrollIntoView){
18441         this.selectedIndex = index;
18442         this.view.select(index);
18443         if(scrollIntoView !== false){
18444             var el = this.view.getNode(index);
18445             /*
18446              * el && !this.multiple && !this.tickable // not sure why we disable multiple before..
18447              */
18448             if(el){
18449                 this.list.scrollChildIntoView(el, false);
18450             }
18451         }
18452     },
18453
18454     // private
18455     selectNext : function(){
18456         var ct = this.store.getCount();
18457         if(ct > 0){
18458             if(this.selectedIndex == -1){
18459                 this.select(0);
18460             }else if(this.selectedIndex < ct-1){
18461                 this.select(this.selectedIndex+1);
18462             }
18463         }
18464     },
18465
18466     // private
18467     selectPrev : function(){
18468         var ct = this.store.getCount();
18469         if(ct > 0){
18470             if(this.selectedIndex == -1){
18471                 this.select(0);
18472             }else if(this.selectedIndex != 0){
18473                 this.select(this.selectedIndex-1);
18474             }
18475         }
18476     },
18477
18478     // private
18479     onKeyUp : function(e){
18480         if(this.editable !== false && !e.isSpecialKey()){
18481             this.lastKey = e.getKey();
18482             this.dqTask.delay(this.queryDelay);
18483         }
18484     },
18485
18486     // private
18487     validateBlur : function(){
18488         return !this.list || !this.list.isVisible();   
18489     },
18490
18491     // private
18492     initQuery : function(){
18493         
18494         var v = this.getRawValue();
18495         
18496         if(this.tickable && this.editable){
18497             v = this.tickableInputEl().getValue();
18498         }
18499         
18500         this.doQuery(v);
18501     },
18502
18503     // private
18504     doForce : function(){
18505         if(this.inputEl().dom.value.length > 0){
18506             this.inputEl().dom.value =
18507                 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
18508              
18509         }
18510     },
18511
18512     /**
18513      * Execute a query to filter the dropdown list.  Fires the beforequery event prior to performing the
18514      * query allowing the query action to be canceled if needed.
18515      * @param {String} query The SQL query to execute
18516      * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
18517      * in the field than the minimum specified by the minChars config option.  It also clears any filter previously
18518      * saved in the current store (defaults to false)
18519      */
18520     doQuery : function(q, forceAll){
18521         
18522         if(q === undefined || q === null){
18523             q = '';
18524         }
18525         var qe = {
18526             query: q,
18527             forceAll: forceAll,
18528             combo: this,
18529             cancel:false
18530         };
18531         if(this.fireEvent('beforequery', qe)===false || qe.cancel){
18532             return false;
18533         }
18534         q = qe.query;
18535         
18536         forceAll = qe.forceAll;
18537         if(forceAll === true || (q.length >= this.minChars)){
18538             
18539             this.hasQuery = true;
18540             
18541             if(this.lastQuery != q || this.alwaysQuery){
18542                 this.lastQuery = q;
18543                 if(this.mode == 'local'){
18544                     this.selectedIndex = -1;
18545                     if(forceAll){
18546                         this.store.clearFilter();
18547                     }else{
18548                         
18549                         if(this.specialFilter){
18550                             this.fireEvent('specialfilter', this);
18551                             this.onLoad();
18552                             return;
18553                         }
18554                         
18555                         this.store.filter(this.displayField, q);
18556                     }
18557                     
18558                     this.store.fireEvent("datachanged", this.store);
18559                     
18560                     this.onLoad();
18561                     
18562                     
18563                 }else{
18564                     
18565                     this.store.baseParams[this.queryParam] = q;
18566                     
18567                     var options = {params : this.getParams(q)};
18568                     
18569                     if(this.loadNext){
18570                         options.add = true;
18571                         options.params.start = this.page * this.pageSize;
18572                     }
18573                     
18574                     this.store.load(options);
18575                     
18576                     /*
18577                      *  this code will make the page width larger, at the beginning, the list not align correctly, 
18578                      *  we should expand the list on onLoad
18579                      *  so command out it
18580                      */
18581 //                    this.expand();
18582                 }
18583             }else{
18584                 this.selectedIndex = -1;
18585                 this.onLoad();   
18586             }
18587         }
18588         
18589         this.loadNext = false;
18590     },
18591     
18592     // private
18593     getParams : function(q){
18594         var p = {};
18595         //p[this.queryParam] = q;
18596         
18597         if(this.pageSize){
18598             p.start = 0;
18599             p.limit = this.pageSize;
18600         }
18601         return p;
18602     },
18603
18604     /**
18605      * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
18606      */
18607     collapse : function(){
18608         if(!this.isExpanded()){
18609             return;
18610         }
18611         
18612         this.list.hide();
18613         
18614         this.hasFocus = false;
18615         
18616         if(this.tickable){
18617             this.okBtn.hide();
18618             this.cancelBtn.hide();
18619             this.trigger.show();
18620             
18621             if(this.editable){
18622                 this.tickableInputEl().dom.value = '';
18623                 this.tickableInputEl().blur();
18624             }
18625             
18626         }
18627         
18628         Roo.get(document).un('mousedown', this.collapseIf, this);
18629         Roo.get(document).un('mousewheel', this.collapseIf, this);
18630         if (!this.editable) {
18631             Roo.get(document).un('keydown', this.listKeyPress, this);
18632         }
18633         this.fireEvent('collapse', this);
18634         
18635         this.validate();
18636     },
18637
18638     // private
18639     collapseIf : function(e){
18640         var in_combo  = e.within(this.el);
18641         var in_list =  e.within(this.list);
18642         var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
18643         
18644         if (in_combo || in_list || is_list) {
18645             //e.stopPropagation();
18646             return;
18647         }
18648         
18649         if(this.tickable){
18650             this.onTickableFooterButtonClick(e, false, false);
18651         }
18652
18653         this.collapse();
18654         
18655     },
18656
18657     /**
18658      * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
18659      */
18660     expand : function(){
18661        
18662         if(this.isExpanded() || !this.hasFocus){
18663             return;
18664         }
18665         
18666         var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
18667         this.list.setWidth(lw);
18668         
18669         Roo.log('expand');
18670         
18671         this.list.show();
18672         
18673         this.restrictHeight();
18674         
18675         if(this.tickable){
18676             
18677             this.tickItems = Roo.apply([], this.item);
18678             
18679             this.okBtn.show();
18680             this.cancelBtn.show();
18681             this.trigger.hide();
18682             
18683             if(this.editable){
18684                 this.tickableInputEl().focus();
18685             }
18686             
18687         }
18688         
18689         Roo.get(document).on('mousedown', this.collapseIf, this);
18690         Roo.get(document).on('mousewheel', this.collapseIf, this);
18691         if (!this.editable) {
18692             Roo.get(document).on('keydown', this.listKeyPress, this);
18693         }
18694         
18695         this.fireEvent('expand', this);
18696     },
18697
18698     // private
18699     // Implements the default empty TriggerField.onTriggerClick function
18700     onTriggerClick : function(e)
18701     {
18702         Roo.log('trigger click');
18703         
18704         if(this.disabled || !this.triggerList){
18705             return;
18706         }
18707         
18708         this.page = 0;
18709         this.loadNext = false;
18710         
18711         if(this.isExpanded()){
18712             this.collapse();
18713             if (!this.blockFocus) {
18714                 this.inputEl().focus();
18715             }
18716             
18717         }else {
18718             this.hasFocus = true;
18719             if(this.triggerAction == 'all') {
18720                 this.doQuery(this.allQuery, true);
18721             } else {
18722                 this.doQuery(this.getRawValue());
18723             }
18724             if (!this.blockFocus) {
18725                 this.inputEl().focus();
18726             }
18727         }
18728     },
18729     
18730     onTickableTriggerClick : function(e)
18731     {
18732         if(this.disabled){
18733             return;
18734         }
18735         
18736         this.page = 0;
18737         this.loadNext = false;
18738         this.hasFocus = true;
18739         
18740         if(this.triggerAction == 'all') {
18741             this.doQuery(this.allQuery, true);
18742         } else {
18743             this.doQuery(this.getRawValue());
18744         }
18745     },
18746     
18747     onSearchFieldClick : function(e)
18748     {
18749         if(this.hasFocus && !this.disabled && e.getTarget().nodeName.toLowerCase() != 'button'){
18750             this.onTickableFooterButtonClick(e, false, false);
18751             return;
18752         }
18753         
18754         if(this.hasFocus || this.disabled || e.getTarget().nodeName.toLowerCase() == 'button'){
18755             return;
18756         }
18757         
18758         this.page = 0;
18759         this.loadNext = false;
18760         this.hasFocus = true;
18761         
18762         if(this.triggerAction == 'all') {
18763             this.doQuery(this.allQuery, true);
18764         } else {
18765             this.doQuery(this.getRawValue());
18766         }
18767     },
18768     
18769     listKeyPress : function(e)
18770     {
18771         //Roo.log('listkeypress');
18772         // scroll to first matching element based on key pres..
18773         if (e.isSpecialKey()) {
18774             return false;
18775         }
18776         var k = String.fromCharCode(e.getKey()).toUpperCase();
18777         //Roo.log(k);
18778         var match  = false;
18779         var csel = this.view.getSelectedNodes();
18780         var cselitem = false;
18781         if (csel.length) {
18782             var ix = this.view.indexOf(csel[0]);
18783             cselitem  = this.store.getAt(ix);
18784             if (!cselitem.get(this.displayField) || cselitem.get(this.displayField).substring(0,1).toUpperCase() != k) {
18785                 cselitem = false;
18786             }
18787             
18788         }
18789         
18790         this.store.each(function(v) { 
18791             if (cselitem) {
18792                 // start at existing selection.
18793                 if (cselitem.id == v.id) {
18794                     cselitem = false;
18795                 }
18796                 return true;
18797             }
18798                 
18799             if (v.get(this.displayField) && v.get(this.displayField).substring(0,1).toUpperCase() == k) {
18800                 match = this.store.indexOf(v);
18801                 return false;
18802             }
18803             return true;
18804         }, this);
18805         
18806         if (match === false) {
18807             return true; // no more action?
18808         }
18809         // scroll to?
18810         this.view.select(match);
18811         var sn = Roo.get(this.view.getSelectedNodes()[0]);
18812         sn.scrollIntoView(sn.dom.parentNode, false);
18813     },
18814     
18815     onViewScroll : function(e, t){
18816         
18817         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){
18818             return;
18819         }
18820         
18821         this.hasQuery = true;
18822         
18823         this.loading = this.list.select('.loading', true).first();
18824         
18825         if(this.loading === null){
18826             this.list.createChild({
18827                 tag: 'div',
18828                 cls: 'loading roo-select2-more-results roo-select2-active',
18829                 html: 'Loading more results...'
18830             });
18831             
18832             this.loading = this.list.select('.loading', true).first();
18833             
18834             this.loading.setVisibilityMode(Roo.Element.DISPLAY);
18835             
18836             this.loading.hide();
18837         }
18838         
18839         this.loading.show();
18840         
18841         var _combo = this;
18842         
18843         this.page++;
18844         this.loadNext = true;
18845         
18846         (function() { _combo.doQuery(_combo.allQuery, true); }).defer(500);
18847         
18848         return;
18849     },
18850     
18851     addItem : function(o)
18852     {   
18853         var dv = ''; // display value
18854         
18855         if (this.displayField) {
18856             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
18857         } else {
18858             // this is an error condition!!!
18859             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
18860         }
18861         
18862         if(!dv.length){
18863             return;
18864         }
18865         
18866         var choice = this.choices.createChild({
18867             tag: 'li',
18868             cls: 'roo-select2-search-choice',
18869             cn: [
18870                 {
18871                     tag: 'div',
18872                     html: dv
18873                 },
18874                 {
18875                     tag: 'a',
18876                     href: '#',
18877                     cls: 'roo-select2-search-choice-close fa fa-times',
18878                     tabindex: '-1'
18879                 }
18880             ]
18881             
18882         }, this.searchField);
18883         
18884         var close = choice.select('a.roo-select2-search-choice-close', true).first();
18885         
18886         close.on('click', this.onRemoveItem, this, { item : choice, data : o} );
18887         
18888         this.item.push(o);
18889         
18890         this.lastData = o;
18891         
18892         this.syncValue();
18893         
18894         this.inputEl().dom.value = '';
18895         
18896         this.validate();
18897     },
18898     
18899     onRemoveItem : function(e, _self, o)
18900     {
18901         e.preventDefault();
18902         
18903         this.lastItem = Roo.apply([], this.item);
18904         
18905         var index = this.item.indexOf(o.data) * 1;
18906         
18907         if( index < 0){
18908             Roo.log('not this item?!');
18909             return;
18910         }
18911         
18912         this.item.splice(index, 1);
18913         o.item.remove();
18914         
18915         this.syncValue();
18916         
18917         this.fireEvent('remove', this, e);
18918         
18919         this.validate();
18920         
18921     },
18922     
18923     syncValue : function()
18924     {
18925         if(!this.item.length){
18926             this.clearValue();
18927             return;
18928         }
18929             
18930         var value = [];
18931         var _this = this;
18932         Roo.each(this.item, function(i){
18933             if(_this.valueField){
18934                 value.push(i[_this.valueField]);
18935                 return;
18936             }
18937
18938             value.push(i);
18939         });
18940
18941         this.value = value.join(',');
18942
18943         if(this.hiddenField){
18944             this.hiddenField.dom.value = this.value;
18945         }
18946         
18947         this.store.fireEvent("datachanged", this.store);
18948         
18949         this.validate();
18950     },
18951     
18952     clearItem : function()
18953     {
18954         if(!this.multiple){
18955             return;
18956         }
18957         
18958         this.item = [];
18959         
18960         Roo.each(this.choices.select('>li.roo-select2-search-choice', true).elements, function(c){
18961            c.remove();
18962         });
18963         
18964         this.syncValue();
18965         
18966         this.validate();
18967         
18968         if(this.tickable && !Roo.isTouch){
18969             this.view.refresh();
18970         }
18971     },
18972     
18973     inputEl: function ()
18974     {
18975         if(Roo.isIOS && this.useNativeIOS){
18976             return this.el.select('select.roo-ios-select', true).first();
18977         }
18978         
18979         if(Roo.isTouch && this.mobileTouchView){
18980             return this.el.select('input.form-control',true).first();
18981         }
18982         
18983         if(this.tickable){
18984             return this.searchField;
18985         }
18986         
18987         return this.el.select('input.form-control',true).first();
18988     },
18989     
18990     onTickableFooterButtonClick : function(e, btn, el)
18991     {
18992         e.preventDefault();
18993         
18994         this.lastItem = Roo.apply([], this.item);
18995         
18996         if(btn && btn.name == 'cancel'){
18997             this.tickItems = Roo.apply([], this.item);
18998             this.collapse();
18999             return;
19000         }
19001         
19002         this.clearItem();
19003         
19004         var _this = this;
19005         
19006         Roo.each(this.tickItems, function(o){
19007             _this.addItem(o);
19008         });
19009         
19010         this.collapse();
19011         
19012     },
19013     
19014     validate : function()
19015     {
19016         if(this.getVisibilityEl().hasClass('hidden')){
19017             return true;
19018         }
19019         
19020         var v = this.getRawValue();
19021         
19022         if(this.multiple){
19023             v = this.getValue();
19024         }
19025         
19026         if(this.disabled || this.allowBlank || v.length){
19027             this.markValid();
19028             return true;
19029         }
19030         
19031         this.markInvalid();
19032         return false;
19033     },
19034     
19035     tickableInputEl : function()
19036     {
19037         if(!this.tickable || !this.editable){
19038             return this.inputEl();
19039         }
19040         
19041         return this.inputEl().select('.roo-select2-search-field-input', true).first();
19042     },
19043     
19044     
19045     getAutoCreateTouchView : function()
19046     {
19047         var id = Roo.id();
19048         
19049         var cfg = {
19050             cls: 'form-group' //input-group
19051         };
19052         
19053         var input =  {
19054             tag: 'input',
19055             id : id,
19056             type : this.inputType,
19057             cls : 'form-control x-combo-noedit',
19058             autocomplete: 'new-password',
19059             placeholder : this.placeholder || '',
19060             readonly : true
19061         };
19062         
19063         if (this.name) {
19064             input.name = this.name;
19065         }
19066         
19067         if (this.size) {
19068             input.cls += ' input-' + this.size;
19069         }
19070         
19071         if (this.disabled) {
19072             input.disabled = true;
19073         }
19074         
19075         var inputblock = {
19076             cls : 'roo-combobox-wrap',
19077             cn : [
19078                 input
19079             ]
19080         };
19081         
19082         if(this.before){
19083             inputblock.cls += ' input-group';
19084             
19085             inputblock.cn.unshift({
19086                 tag :'span',
19087                 cls : 'input-group-addon input-group-prepend input-group-text',
19088                 html : this.before
19089             });
19090         }
19091         
19092         if(this.removable && !this.multiple){
19093             inputblock.cls += ' roo-removable';
19094             
19095             inputblock.cn.push({
19096                 tag: 'button',
19097                 html : 'x',
19098                 cls : 'roo-combo-removable-btn close'
19099             });
19100         }
19101
19102         if(this.hasFeedback && !this.allowBlank){
19103             
19104             inputblock.cls += ' has-feedback';
19105             
19106             inputblock.cn.push({
19107                 tag: 'span',
19108                 cls: 'glyphicon form-control-feedback'
19109             });
19110             
19111         }
19112         
19113         if (this.after) {
19114             
19115             inputblock.cls += (this.before) ? '' : ' input-group';
19116             
19117             inputblock.cn.push({
19118                 tag :'span',
19119                 cls : 'input-group-addon input-group-append input-group-text',
19120                 html : this.after
19121             });
19122         }
19123
19124         
19125         var ibwrap = inputblock;
19126         
19127         if(this.multiple){
19128             ibwrap = {
19129                 tag: 'ul',
19130                 cls: 'roo-select2-choices',
19131                 cn:[
19132                     {
19133                         tag: 'li',
19134                         cls: 'roo-select2-search-field',
19135                         cn: [
19136
19137                             inputblock
19138                         ]
19139                     }
19140                 ]
19141             };
19142         
19143             
19144         }
19145         
19146         var combobox = {
19147             cls: 'roo-select2-container input-group roo-touchview-combobox ',
19148             cn: [
19149                 {
19150                     tag: 'input',
19151                     type : 'hidden',
19152                     cls: 'form-hidden-field'
19153                 },
19154                 ibwrap
19155             ]
19156         };
19157         
19158         if(!this.multiple && this.showToggleBtn){
19159             
19160             var caret = {
19161                 cls: 'caret'
19162             };
19163             
19164             if (this.caret != false) {
19165                 caret = {
19166                      tag: 'i',
19167                      cls: 'fa fa-' + this.caret
19168                 };
19169                 
19170             }
19171             
19172             combobox.cn.push({
19173                 tag :'span',
19174                 cls : 'input-group-addon input-group-append input-group-text btn dropdown-toggle',
19175                 cn : [
19176                     Roo.bootstrap.version == 3 ? caret : '',
19177                     {
19178                         tag: 'span',
19179                         cls: 'combobox-clear',
19180                         cn  : [
19181                             {
19182                                 tag : 'i',
19183                                 cls: 'icon-remove'
19184                             }
19185                         ]
19186                     }
19187                 ]
19188
19189             })
19190         }
19191         
19192         if(this.multiple){
19193             combobox.cls += ' roo-select2-container-multi';
19194         }
19195         
19196         var required =  this.allowBlank ?  {
19197                     tag : 'i',
19198                     style: 'display: none'
19199                 } : {
19200                    tag : 'i',
19201                    cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
19202                    tooltip : 'This field is required'
19203                 };
19204         
19205         var align = this.labelAlign || this.parentLabelAlign();
19206         
19207         if (align ==='left' && this.fieldLabel.length) {
19208
19209             cfg.cn = [
19210                 required,
19211                 {
19212                     tag: 'label',
19213                     cls : 'control-label col-form-label',
19214                     html : this.fieldLabel
19215
19216                 },
19217                 {
19218                     cls : 'roo-combobox-wrap ', 
19219                     cn: [
19220                         combobox
19221                     ]
19222                 }
19223             ];
19224             
19225             var labelCfg = cfg.cn[1];
19226             var contentCfg = cfg.cn[2];
19227             
19228
19229             if(this.indicatorpos == 'right'){
19230                 cfg.cn = [
19231                     {
19232                         tag: 'label',
19233                         'for' :  id,
19234                         cls : 'control-label col-form-label',
19235                         cn : [
19236                             {
19237                                 tag : 'span',
19238                                 html : this.fieldLabel
19239                             },
19240                             required
19241                         ]
19242                     },
19243                     {
19244                         cls : "roo-combobox-wrap ",
19245                         cn: [
19246                             combobox
19247                         ]
19248                     }
19249
19250                 ];
19251                 
19252                 labelCfg = cfg.cn[0];
19253                 contentCfg = cfg.cn[1];
19254             }
19255             
19256            
19257             
19258             if(this.labelWidth > 12){
19259                 labelCfg.style = "width: " + this.labelWidth + 'px';
19260             }
19261            
19262             if(this.labelWidth < 13 && this.labelmd == 0){
19263                 this.labelmd = this.labelWidth;
19264             }
19265             
19266             if(this.labellg > 0){
19267                 labelCfg.cls += ' col-lg-' + this.labellg;
19268                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
19269             }
19270             
19271             if(this.labelmd > 0){
19272                 labelCfg.cls += ' col-md-' + this.labelmd;
19273                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
19274             }
19275             
19276             if(this.labelsm > 0){
19277                 labelCfg.cls += ' col-sm-' + this.labelsm;
19278                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
19279             }
19280             
19281             if(this.labelxs > 0){
19282                 labelCfg.cls += ' col-xs-' + this.labelxs;
19283                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
19284             }
19285                 
19286                 
19287         } else if ( this.fieldLabel.length) {
19288             cfg.cn = [
19289                required,
19290                 {
19291                     tag: 'label',
19292                     cls : 'control-label',
19293                     html : this.fieldLabel
19294
19295                 },
19296                 {
19297                     cls : '', 
19298                     cn: [
19299                         combobox
19300                     ]
19301                 }
19302             ];
19303             
19304             if(this.indicatorpos == 'right'){
19305                 cfg.cn = [
19306                     {
19307                         tag: 'label',
19308                         cls : 'control-label',
19309                         html : this.fieldLabel,
19310                         cn : [
19311                             required
19312                         ]
19313                     },
19314                     {
19315                         cls : '', 
19316                         cn: [
19317                             combobox
19318                         ]
19319                     }
19320                 ];
19321             }
19322         } else {
19323             cfg.cn = combobox;    
19324         }
19325         
19326         
19327         var settings = this;
19328         
19329         ['xs','sm','md','lg'].map(function(size){
19330             if (settings[size]) {
19331                 cfg.cls += ' col-' + size + '-' + settings[size];
19332             }
19333         });
19334         
19335         return cfg;
19336     },
19337     
19338     initTouchView : function()
19339     {
19340         this.renderTouchView();
19341         
19342         this.touchViewEl.on('scroll', function(){
19343             this.el.dom.scrollTop = 0;
19344         }, this);
19345         
19346         this.originalValue = this.getValue();
19347         
19348         this.triggerEl = this.el.select('span.dropdown-toggle',true).first();
19349         
19350         this.inputEl().on("click", this.showTouchView, this);
19351         if (this.triggerEl) {
19352             this.triggerEl.on("click", this.showTouchView, this);
19353         }
19354         
19355         
19356         this.touchViewFooterEl.select('.roo-touch-view-cancel', true).first().on('click', this.hideTouchView, this);
19357         this.touchViewFooterEl.select('.roo-touch-view-ok', true).first().on('click', this.setTouchViewValue, this);
19358         
19359         this.maskEl = new Roo.LoadMask(this.touchViewEl, { store : this.store, msgCls: 'roo-el-mask-msg' });
19360         
19361         this.store.on('beforeload', this.onTouchViewBeforeLoad, this);
19362         this.store.on('load', this.onTouchViewLoad, this);
19363         this.store.on('loadexception', this.onTouchViewLoadException, this);
19364         
19365         if(this.hiddenName){
19366             
19367             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
19368             
19369             this.hiddenField.dom.value =
19370                 this.hiddenValue !== undefined ? this.hiddenValue :
19371                 this.value !== undefined ? this.value : '';
19372         
19373             this.el.dom.removeAttribute('name');
19374             this.hiddenField.dom.setAttribute('name', this.hiddenName);
19375         }
19376         
19377         if(this.multiple){
19378             this.choices = this.el.select('ul.roo-select2-choices', true).first();
19379             this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
19380         }
19381         
19382         if(this.removable && !this.multiple){
19383             var close = this.closeTriggerEl();
19384             if(close){
19385                 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
19386                 close.on('click', this.removeBtnClick, this, close);
19387             }
19388         }
19389         /*
19390          * fix the bug in Safari iOS8
19391          */
19392         this.inputEl().on("focus", function(e){
19393             document.activeElement.blur();
19394         }, this);
19395         
19396         this._touchViewMask = Roo.DomHelper.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
19397         
19398         return;
19399         
19400         
19401     },
19402     
19403     renderTouchView : function()
19404     {
19405         this.touchViewEl = Roo.get(document.body).createChild(Roo.bootstrap.form.ComboBox.touchViewTemplate);
19406         this.touchViewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
19407         
19408         this.touchViewHeaderEl = this.touchViewEl.select('.modal-header', true).first();
19409         this.touchViewHeaderEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
19410         
19411         this.touchViewBodyEl = this.touchViewEl.select('.modal-body', true).first();
19412         this.touchViewBodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
19413         this.touchViewBodyEl.setStyle('overflow', 'auto');
19414         
19415         this.touchViewListGroup = this.touchViewBodyEl.select('.list-group', true).first();
19416         this.touchViewListGroup.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
19417         
19418         this.touchViewFooterEl = this.touchViewEl.select('.modal-footer', true).first();
19419         this.touchViewFooterEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
19420         
19421     },
19422     
19423     showTouchView : function()
19424     {
19425         if(this.disabled){
19426             return;
19427         }
19428         
19429         this.touchViewHeaderEl.hide();
19430
19431         if(this.modalTitle.length){
19432             this.touchViewHeaderEl.dom.innerHTML = this.modalTitle;
19433             this.touchViewHeaderEl.show();
19434         }
19435
19436         this.touchViewEl.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
19437         this.touchViewEl.show();
19438
19439         this.touchViewEl.select('.modal-dialog', true).first().setStyle({ margin : '0px', width : '100%'});
19440         
19441         //this.touchViewEl.select('.modal-dialog > .modal-content', true).first().setSize(
19442         //        Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
19443
19444         var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
19445
19446         if(this.modalTitle.length){
19447             bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
19448         }
19449         
19450         this.touchViewBodyEl.setHeight(bodyHeight);
19451
19452         if(this.animate){
19453             var _this = this;
19454             (function(){ _this.touchViewEl.addClass(['in','show']); }).defer(50);
19455         }else{
19456             this.touchViewEl.addClass(['in','show']);
19457         }
19458         
19459         if(this._touchViewMask){
19460             Roo.get(document.body).addClass("x-body-masked");
19461             this._touchViewMask.setSize(Roo.lib.Dom.getViewWidth(true),   Roo.lib.Dom.getViewHeight(true));
19462             this._touchViewMask.setStyle('z-index', 10000);
19463             this._touchViewMask.addClass('show');
19464         }
19465         
19466         this.doTouchViewQuery();
19467         
19468     },
19469     
19470     hideTouchView : function()
19471     {
19472         this.touchViewEl.removeClass(['in','show']);
19473
19474         if(this.animate){
19475             var _this = this;
19476             (function(){ _this.touchViewEl.setStyle('display', 'none'); }).defer(150);
19477         }else{
19478             this.touchViewEl.setStyle('display', 'none');
19479         }
19480         
19481         if(this._touchViewMask){
19482             this._touchViewMask.removeClass('show');
19483             Roo.get(document.body).removeClass("x-body-masked");
19484         }
19485     },
19486     
19487     setTouchViewValue : function()
19488     {
19489         if(this.multiple){
19490             this.clearItem();
19491         
19492             var _this = this;
19493
19494             Roo.each(this.tickItems, function(o){
19495                 this.addItem(o);
19496             }, this);
19497         }
19498         
19499         this.hideTouchView();
19500     },
19501     
19502     doTouchViewQuery : function()
19503     {
19504         var qe = {
19505             query: '',
19506             forceAll: true,
19507             combo: this,
19508             cancel:false
19509         };
19510         
19511         if(this.fireEvent('beforequery', qe) ===false || qe.cancel){
19512             return false;
19513         }
19514         
19515         if(!this.alwaysQuery || this.mode == 'local'){
19516             this.onTouchViewLoad();
19517             return;
19518         }
19519         
19520         this.store.load();
19521     },
19522     
19523     onTouchViewBeforeLoad : function(combo,opts)
19524     {
19525         return;
19526     },
19527
19528     // private
19529     onTouchViewLoad : function()
19530     {
19531         if(this.store.getCount() < 1){
19532             this.onTouchViewEmptyResults();
19533             return;
19534         }
19535         
19536         this.clearTouchView();
19537         
19538         var rawValue = this.getRawValue();
19539         
19540         var template = (this.multiple) ? Roo.bootstrap.form.ComboBox.listItemCheckbox : Roo.bootstrap.form.ComboBox.listItemRadio;
19541         
19542         this.tickItems = [];
19543         
19544         this.store.data.each(function(d, rowIndex){
19545             var row = this.touchViewListGroup.createChild(template);
19546             
19547             if(typeof(d.data.cls) != 'undefined' && d.data.cls.length){
19548                 row.addClass(d.data.cls);
19549             }
19550             
19551             if(this.displayField && typeof(d.data[this.displayField]) != 'undefined'){
19552                 var cfg = {
19553                     data : d.data,
19554                     html : d.data[this.displayField]
19555                 };
19556                 
19557                 if(this.fireEvent('touchviewdisplay', this, cfg) !== false){
19558                     row.select('.roo-combobox-list-group-item-value', true).first().dom.innerHTML = cfg.html;
19559                 }
19560             }
19561             row.removeClass('selected');
19562             if(!this.multiple && this.valueField &&
19563                     typeof(d.data[this.valueField]) != 'undefined' && d.data[this.valueField] == this.getValue())
19564             {
19565                 // radio buttons..
19566                 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
19567                 row.addClass('selected');
19568             }
19569             
19570             if(this.multiple && this.valueField &&
19571                     typeof(d.data[this.valueField]) != 'undefined' && this.getValue().indexOf(d.data[this.valueField]) != -1)
19572             {
19573                 
19574                 // checkboxes...
19575                 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
19576                 this.tickItems.push(d.data);
19577             }
19578             
19579             row.on('click', this.onTouchViewClick, this, {row : row, rowIndex : rowIndex});
19580             
19581         }, this);
19582         
19583         var firstChecked = this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).first();
19584         
19585         var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
19586
19587         if(this.modalTitle.length){
19588             bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
19589         }
19590
19591         var listHeight = this.touchViewListGroup.getHeight() + this.touchViewBodyEl.getPadding('tb') * 2;
19592         
19593         if(this.mobile_restrict_height && listHeight < bodyHeight){
19594             this.touchViewBodyEl.setHeight(listHeight);
19595         }
19596         
19597         var _this = this;
19598         
19599         if(firstChecked && listHeight > bodyHeight){
19600             (function() { firstChecked.findParent('li').scrollIntoView(_this.touchViewListGroup.dom); }).defer(500);
19601         }
19602         
19603     },
19604     
19605     onTouchViewLoadException : function()
19606     {
19607         this.hideTouchView();
19608     },
19609     
19610     onTouchViewEmptyResults : function()
19611     {
19612         this.clearTouchView();
19613         
19614         this.touchViewListGroup.createChild(Roo.bootstrap.form.ComboBox.emptyResult);
19615         
19616         this.touchViewListGroup.select('.roo-combobox-touch-view-empty-result', true).first().dom.innerHTML = this.emptyResultText;
19617         
19618     },
19619     
19620     clearTouchView : function()
19621     {
19622         this.touchViewListGroup.dom.innerHTML = '';
19623     },
19624     
19625     onTouchViewClick : function(e, el, o)
19626     {
19627         e.preventDefault();
19628         
19629         var row = o.row;
19630         var rowIndex = o.rowIndex;
19631         
19632         var r = this.store.getAt(rowIndex);
19633         
19634         if(this.fireEvent('beforeselect', this, r, rowIndex) !== false){
19635             
19636             if(!this.multiple){
19637                 Roo.each(this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).elements, function(c){
19638                     c.dom.removeAttribute('checked');
19639                 }, this);
19640
19641                 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
19642
19643                 this.setFromData(r.data);
19644
19645                 var close = this.closeTriggerEl();
19646
19647                 if(close){
19648                     close.show();
19649                 }
19650
19651                 this.hideTouchView();
19652
19653                 this.fireEvent('select', this, r, rowIndex);
19654
19655                 return;
19656             }
19657
19658             if(this.valueField && typeof(r.data[this.valueField]) != 'undefined' && this.getValue().indexOf(r.data[this.valueField]) != -1){
19659                 row.select('.roo-combobox-list-group-item-box > input', true).first().dom.removeAttribute('checked');
19660                 this.tickItems.splice(this.tickItems.indexOf(r.data), 1);
19661                 return;
19662             }
19663
19664             row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
19665             this.addItem(r.data);
19666             this.tickItems.push(r.data);
19667         }
19668     },
19669     
19670     getAutoCreateNativeIOS : function()
19671     {
19672         var cfg = {
19673             cls: 'form-group' //input-group,
19674         };
19675         
19676         var combobox =  {
19677             tag: 'select',
19678             cls : 'roo-ios-select'
19679         };
19680         
19681         if (this.name) {
19682             combobox.name = this.name;
19683         }
19684         
19685         if (this.disabled) {
19686             combobox.disabled = true;
19687         }
19688         
19689         var settings = this;
19690         
19691         ['xs','sm','md','lg'].map(function(size){
19692             if (settings[size]) {
19693                 cfg.cls += ' col-' + size + '-' + settings[size];
19694             }
19695         });
19696         
19697         cfg.cn = combobox;
19698         
19699         return cfg;
19700         
19701     },
19702     
19703     initIOSView : function()
19704     {
19705         this.store.on('load', this.onIOSViewLoad, this);
19706         
19707         return;
19708     },
19709     
19710     onIOSViewLoad : function()
19711     {
19712         if(this.store.getCount() < 1){
19713             return;
19714         }
19715         
19716         this.clearIOSView();
19717         
19718         if(this.allowBlank) {
19719             
19720             var default_text = '-- SELECT --';
19721             
19722             if(this.placeholder.length){
19723                 default_text = this.placeholder;
19724             }
19725             
19726             if(this.emptyTitle.length){
19727                 default_text += ' - ' + this.emptyTitle + ' -';
19728             }
19729             
19730             var opt = this.inputEl().createChild({
19731                 tag: 'option',
19732                 value : 0,
19733                 html : default_text
19734             });
19735             
19736             var o = {};
19737             o[this.valueField] = 0;
19738             o[this.displayField] = default_text;
19739             
19740             this.ios_options.push({
19741                 data : o,
19742                 el : opt
19743             });
19744             
19745         }
19746         
19747         this.store.data.each(function(d, rowIndex){
19748             
19749             var html = '';
19750             
19751             if(this.displayField && typeof(d.data[this.displayField]) != 'undefined'){
19752                 html = d.data[this.displayField];
19753             }
19754             
19755             var value = '';
19756             
19757             if(this.valueField && typeof(d.data[this.valueField]) != 'undefined'){
19758                 value = d.data[this.valueField];
19759             }
19760             
19761             var option = {
19762                 tag: 'option',
19763                 value : value,
19764                 html : html
19765             };
19766             
19767             if(this.value == d.data[this.valueField]){
19768                 option['selected'] = true;
19769             }
19770             
19771             var opt = this.inputEl().createChild(option);
19772             
19773             this.ios_options.push({
19774                 data : d.data,
19775                 el : opt
19776             });
19777             
19778         }, this);
19779         
19780         this.inputEl().on('change', function(){
19781            this.fireEvent('select', this);
19782         }, this);
19783         
19784     },
19785     
19786     clearIOSView: function()
19787     {
19788         this.inputEl().dom.innerHTML = '';
19789         
19790         this.ios_options = [];
19791     },
19792     
19793     setIOSValue: function(v)
19794     {
19795         this.value = v;
19796         
19797         if(!this.ios_options){
19798             return;
19799         }
19800         
19801         Roo.each(this.ios_options, function(opts){
19802            
19803            opts.el.dom.removeAttribute('selected');
19804            
19805            if(opts.data[this.valueField] != v){
19806                return;
19807            }
19808            
19809            opts.el.dom.setAttribute('selected', true);
19810            
19811         }, this);
19812     }
19813
19814     /** 
19815     * @cfg {Boolean} grow 
19816     * @hide 
19817     */
19818     /** 
19819     * @cfg {Number} growMin 
19820     * @hide 
19821     */
19822     /** 
19823     * @cfg {Number} growMax 
19824     * @hide 
19825     */
19826     /**
19827      * @hide
19828      * @method autoSize
19829      */
19830 });
19831
19832 Roo.apply(Roo.bootstrap.form.ComboBox,  {
19833     
19834     header : {
19835         tag: 'div',
19836         cls: 'modal-header',
19837         cn: [
19838             {
19839                 tag: 'h4',
19840                 cls: 'modal-title'
19841             }
19842         ]
19843     },
19844     
19845     body : {
19846         tag: 'div',
19847         cls: 'modal-body',
19848         cn: [
19849             {
19850                 tag: 'ul',
19851                 cls: 'list-group'
19852             }
19853         ]
19854     },
19855     
19856     listItemRadio : {
19857         tag: 'li',
19858         cls: 'list-group-item',
19859         cn: [
19860             {
19861                 tag: 'span',
19862                 cls: 'roo-combobox-list-group-item-value'
19863             },
19864             {
19865                 tag: 'div',
19866                 cls: 'roo-combobox-list-group-item-box pull-xs-right radio-inline radio radio-info',
19867                 cn: [
19868                     {
19869                         tag: 'input',
19870                         type: 'radio'
19871                     },
19872                     {
19873                         tag: 'label'
19874                     }
19875                 ]
19876             }
19877         ]
19878     },
19879     
19880     listItemCheckbox : {
19881         tag: 'li',
19882         cls: 'list-group-item',
19883         cn: [
19884             {
19885                 tag: 'span',
19886                 cls: 'roo-combobox-list-group-item-value'
19887             },
19888             {
19889                 tag: 'div',
19890                 cls: 'roo-combobox-list-group-item-box pull-xs-right checkbox-inline checkbox checkbox-info',
19891                 cn: [
19892                     {
19893                         tag: 'input',
19894                         type: 'checkbox'
19895                     },
19896                     {
19897                         tag: 'label'
19898                     }
19899                 ]
19900             }
19901         ]
19902     },
19903     
19904     emptyResult : {
19905         tag: 'div',
19906         cls: 'alert alert-danger roo-combobox-touch-view-empty-result'
19907     },
19908     
19909     footer : {
19910         tag: 'div',
19911         cls: 'modal-footer',
19912         cn: [
19913             {
19914                 tag: 'div',
19915                 cls: 'row',
19916                 cn: [
19917                     {
19918                         tag: 'div',
19919                         cls: 'col-xs-6 text-left',
19920                         cn: {
19921                             tag: 'button',
19922                             cls: 'btn btn-danger roo-touch-view-cancel',
19923                             html: 'Cancel'
19924                         }
19925                     },
19926                     {
19927                         tag: 'div',
19928                         cls: 'col-xs-6 text-right',
19929                         cn: {
19930                             tag: 'button',
19931                             cls: 'btn btn-success roo-touch-view-ok',
19932                             html: 'OK'
19933                         }
19934                     }
19935                 ]
19936             }
19937         ]
19938         
19939     }
19940 });
19941
19942 Roo.apply(Roo.bootstrap.form.ComboBox,  {
19943     
19944     touchViewTemplate : {
19945         tag: 'div',
19946         cls: 'modal fade roo-combobox-touch-view',
19947         cn: [
19948             {
19949                 tag: 'div',
19950                 cls: 'modal-dialog',
19951                 style : 'position:fixed', // we have to fix position....
19952                 cn: [
19953                     {
19954                         tag: 'div',
19955                         cls: 'modal-content',
19956                         cn: [
19957                             Roo.bootstrap.form.ComboBox.header,
19958                             Roo.bootstrap.form.ComboBox.body,
19959                             Roo.bootstrap.form.ComboBox.footer
19960                         ]
19961                     }
19962                 ]
19963             }
19964         ]
19965     }
19966 });/*
19967  * Based on:
19968  * Ext JS Library 1.1.1
19969  * Copyright(c) 2006-2007, Ext JS, LLC.
19970  *
19971  * Originally Released Under LGPL - original licence link has changed is not relivant.
19972  *
19973  * Fork - LGPL
19974  * <script type="text/javascript">
19975  */
19976
19977 /**
19978  * @class Roo.View
19979  * @extends Roo.util.Observable
19980  * Create a "View" for an element based on a data model or UpdateManager and the supplied DomHelper template. 
19981  * This class also supports single and multi selection modes. <br>
19982  * Create a data model bound view:
19983  <pre><code>
19984  var store = new Roo.data.Store(...);
19985
19986  var view = new Roo.View({
19987     el : "my-element",
19988     tpl : '&lt;div id="{0}"&gt;{2} - {1}&lt;/div&gt;', // auto create template
19989  
19990     singleSelect: true,
19991     selectedClass: "ydataview-selected",
19992     store: store
19993  });
19994
19995  // listen for node click?
19996  view.on("click", function(vw, index, node, e){
19997  alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
19998  });
19999
20000  // load XML data
20001  dataModel.load("foobar.xml");
20002  </code></pre>
20003  For an example of creating a JSON/UpdateManager view, see {@link Roo.JsonView}.
20004  * <br><br>
20005  * <b>Note: The root of your template must be a single node. Table/row implementations may work but are not supported due to
20006  * IE"s limited insertion support with tables and Opera"s faulty event bubbling.</b>
20007  * 
20008  * Note: old style constructor is still suported (container, template, config)
20009  * 
20010  * @constructor
20011  * Create a new View
20012  * @param {Object} config The config object
20013  * 
20014  */
20015 Roo.View = function(config, depreciated_tpl, depreciated_config){
20016     
20017     this.parent = false;
20018     
20019     if (typeof(depreciated_tpl) == 'undefined') {
20020         // new way.. - universal constructor.
20021         Roo.apply(this, config);
20022         this.el  = Roo.get(this.el);
20023     } else {
20024         // old format..
20025         this.el  = Roo.get(config);
20026         this.tpl = depreciated_tpl;
20027         Roo.apply(this, depreciated_config);
20028     }
20029     this.wrapEl  = this.el.wrap().wrap();
20030     ///this.el = this.wrapEla.appendChild(document.createElement("div"));
20031     
20032     
20033     if(typeof(this.tpl) == "string"){
20034         this.tpl = new Roo.Template(this.tpl);
20035     } else {
20036         // support xtype ctors..
20037         this.tpl = new Roo.factory(this.tpl, Roo);
20038     }
20039     
20040     
20041     this.tpl.compile();
20042     
20043     /** @private */
20044     this.addEvents({
20045         /**
20046          * @event beforeclick
20047          * Fires before a click is processed. Returns false to cancel the default action.
20048          * @param {Roo.View} this
20049          * @param {Number} index The index of the target node
20050          * @param {HTMLElement} node The target node
20051          * @param {Roo.EventObject} e The raw event object
20052          */
20053             "beforeclick" : true,
20054         /**
20055          * @event click
20056          * Fires when a template node is clicked.
20057          * @param {Roo.View} this
20058          * @param {Number} index The index of the target node
20059          * @param {HTMLElement} node The target node
20060          * @param {Roo.EventObject} e The raw event object
20061          */
20062             "click" : true,
20063         /**
20064          * @event dblclick
20065          * Fires when a template node is double clicked.
20066          * @param {Roo.View} this
20067          * @param {Number} index The index of the target node
20068          * @param {HTMLElement} node The target node
20069          * @param {Roo.EventObject} e The raw event object
20070          */
20071             "dblclick" : true,
20072         /**
20073          * @event contextmenu
20074          * Fires when a template node is right clicked.
20075          * @param {Roo.View} this
20076          * @param {Number} index The index of the target node
20077          * @param {HTMLElement} node The target node
20078          * @param {Roo.EventObject} e The raw event object
20079          */
20080             "contextmenu" : true,
20081         /**
20082          * @event selectionchange
20083          * Fires when the selected nodes change.
20084          * @param {Roo.View} this
20085          * @param {Array} selections Array of the selected nodes
20086          */
20087             "selectionchange" : true,
20088     
20089         /**
20090          * @event beforeselect
20091          * Fires before a selection is made. If any handlers return false, the selection is cancelled.
20092          * @param {Roo.View} this
20093          * @param {HTMLElement} node The node to be selected
20094          * @param {Array} selections Array of currently selected nodes
20095          */
20096             "beforeselect" : true,
20097         /**
20098          * @event preparedata
20099          * Fires on every row to render, to allow you to change the data.
20100          * @param {Roo.View} this
20101          * @param {Object} data to be rendered (change this)
20102          */
20103           "preparedata" : true
20104           
20105           
20106         });
20107
20108
20109
20110     this.el.on({
20111         "click": this.onClick,
20112         "dblclick": this.onDblClick,
20113         "contextmenu": this.onContextMenu,
20114         scope:this
20115     });
20116
20117     this.selections = [];
20118     this.nodes = [];
20119     this.cmp = new Roo.CompositeElementLite([]);
20120     if(this.store){
20121         this.store = Roo.factory(this.store, Roo.data);
20122         this.setStore(this.store, true);
20123     }
20124     
20125     if ( this.footer && this.footer.xtype) {
20126            
20127          var fctr = this.wrapEl.appendChild(document.createElement("div"));
20128         
20129         this.footer.dataSource = this.store;
20130         this.footer.container = fctr;
20131         this.footer = Roo.factory(this.footer, Roo);
20132         fctr.insertFirst(this.el);
20133         
20134         // this is a bit insane - as the paging toolbar seems to detach the el..
20135 //        dom.parentNode.parentNode.parentNode
20136          // they get detached?
20137     }
20138     
20139     
20140     Roo.View.superclass.constructor.call(this);
20141     
20142     
20143 };
20144
20145 Roo.extend(Roo.View, Roo.util.Observable, {
20146     
20147      /**
20148      * @cfg {Roo.data.Store} store Data store to load data from.
20149      */
20150     store : false,
20151     
20152     /**
20153      * @cfg {String|Roo.Element} el The container element.
20154      */
20155     el : '',
20156     
20157     /**
20158      * @cfg {String|Roo.Template} tpl The template used by this View 
20159      */
20160     tpl : false,
20161     /**
20162      * @cfg {String} dataName the named area of the template to use as the data area
20163      *                          Works with domtemplates roo-name="name"
20164      */
20165     dataName: false,
20166     /**
20167      * @cfg {String} selectedClass The css class to add to selected nodes
20168      */
20169     selectedClass : "x-view-selected",
20170      /**
20171      * @cfg {String} emptyText The empty text to show when nothing is loaded.
20172      */
20173     emptyText : "",
20174     
20175     /**
20176      * @cfg {String} text to display on mask (default Loading)
20177      */
20178     mask : false,
20179     /**
20180      * @cfg {Boolean} multiSelect Allow multiple selection
20181      */
20182     multiSelect : false,
20183     /**
20184      * @cfg {Boolean} singleSelect Allow single selection
20185      */
20186     singleSelect:  false,
20187     
20188     /**
20189      * @cfg {Boolean} toggleSelect - selecting 
20190      */
20191     toggleSelect : false,
20192     
20193     /**
20194      * @cfg {Boolean} tickable - selecting 
20195      */
20196     tickable : false,
20197     
20198     /**
20199      * Returns the element this view is bound to.
20200      * @return {Roo.Element}
20201      */
20202     getEl : function(){
20203         return this.wrapEl;
20204     },
20205     
20206     
20207
20208     /**
20209      * Refreshes the view. - called by datachanged on the store. - do not call directly.
20210      */
20211     refresh : function(){
20212         //Roo.log('refresh');
20213         var t = this.tpl;
20214         
20215         // if we are using something like 'domtemplate', then
20216         // the what gets used is:
20217         // t.applySubtemplate(NAME, data, wrapping data..)
20218         // the outer template then get' applied with
20219         //     the store 'extra data'
20220         // and the body get's added to the
20221         //      roo-name="data" node?
20222         //      <span class='roo-tpl-{name}'></span> ?????
20223         
20224         
20225         
20226         this.clearSelections();
20227         this.el.update("");
20228         var html = [];
20229         var records = this.store.getRange();
20230         if(records.length < 1) {
20231             
20232             // is this valid??  = should it render a template??
20233             
20234             this.el.update(this.emptyText);
20235             return;
20236         }
20237         var el = this.el;
20238         if (this.dataName) {
20239             this.el.update(t.apply(this.store.meta)); //????
20240             el = this.el.child('.roo-tpl-' + this.dataName);
20241         }
20242         
20243         for(var i = 0, len = records.length; i < len; i++){
20244             var data = this.prepareData(records[i].data, i, records[i]);
20245             this.fireEvent("preparedata", this, data, i, records[i]);
20246             
20247             var d = Roo.apply({}, data);
20248             
20249             if(this.tickable){
20250                 Roo.apply(d, {'roo-id' : Roo.id()});
20251                 
20252                 var _this = this;
20253             
20254                 Roo.each(this.parent.item, function(item){
20255                     if(item[_this.parent.valueField] != data[_this.parent.valueField]){
20256                         return;
20257                     }
20258                     Roo.apply(d, {'roo-data-checked' : 'checked'});
20259                 });
20260             }
20261             
20262             html[html.length] = Roo.util.Format.trim(
20263                 this.dataName ?
20264                     t.applySubtemplate(this.dataName, d, this.store.meta) :
20265                     t.apply(d)
20266             );
20267         }
20268         
20269         
20270         
20271         el.update(html.join(""));
20272         this.nodes = el.dom.childNodes;
20273         this.updateIndexes(0);
20274     },
20275     
20276
20277     /**
20278      * Function to override to reformat the data that is sent to
20279      * the template for each node.
20280      * DEPRICATED - use the preparedata event handler.
20281      * @param {Array/Object} data The raw data (array of colData for a data model bound view or
20282      * a JSON object for an UpdateManager bound view).
20283      */
20284     prepareData : function(data, index, record)
20285     {
20286         this.fireEvent("preparedata", this, data, index, record);
20287         return data;
20288     },
20289
20290     onUpdate : function(ds, record){
20291         // Roo.log('on update');   
20292         this.clearSelections();
20293         var index = this.store.indexOf(record);
20294         var n = this.nodes[index];
20295         this.tpl.insertBefore(n, this.prepareData(record.data, index, record));
20296         n.parentNode.removeChild(n);
20297         this.updateIndexes(index, index);
20298     },
20299
20300     
20301     
20302 // --------- FIXME     
20303     onAdd : function(ds, records, index)
20304     {
20305         //Roo.log(['on Add', ds, records, index] );        
20306         this.clearSelections();
20307         if(this.nodes.length == 0){
20308             this.refresh();
20309             return;
20310         }
20311         var n = this.nodes[index];
20312         for(var i = 0, len = records.length; i < len; i++){
20313             var d = this.prepareData(records[i].data, i, records[i]);
20314             if(n){
20315                 this.tpl.insertBefore(n, d);
20316             }else{
20317                 
20318                 this.tpl.append(this.el, d);
20319             }
20320         }
20321         this.updateIndexes(index);
20322     },
20323
20324     onRemove : function(ds, record, index){
20325        // Roo.log('onRemove');
20326         this.clearSelections();
20327         var el = this.dataName  ?
20328             this.el.child('.roo-tpl-' + this.dataName) :
20329             this.el; 
20330         
20331         el.dom.removeChild(this.nodes[index]);
20332         this.updateIndexes(index);
20333     },
20334
20335     /**
20336      * Refresh an individual node.
20337      * @param {Number} index
20338      */
20339     refreshNode : function(index){
20340         this.onUpdate(this.store, this.store.getAt(index));
20341     },
20342
20343     updateIndexes : function(startIndex, endIndex){
20344         var ns = this.nodes;
20345         startIndex = startIndex || 0;
20346         endIndex = endIndex || ns.length - 1;
20347         for(var i = startIndex; i <= endIndex; i++){
20348             ns[i].nodeIndex = i;
20349         }
20350     },
20351
20352     /**
20353      * Changes the data store this view uses and refresh the view.
20354      * @param {Store} store
20355      */
20356     setStore : function(store, initial){
20357         if(!initial && this.store){
20358             this.store.un("datachanged", this.refresh);
20359             this.store.un("add", this.onAdd);
20360             this.store.un("remove", this.onRemove);
20361             this.store.un("update", this.onUpdate);
20362             this.store.un("clear", this.refresh);
20363             this.store.un("beforeload", this.onBeforeLoad);
20364             this.store.un("load", this.onLoad);
20365             this.store.un("loadexception", this.onLoad);
20366         }
20367         if(store){
20368           
20369             store.on("datachanged", this.refresh, this);
20370             store.on("add", this.onAdd, this);
20371             store.on("remove", this.onRemove, this);
20372             store.on("update", this.onUpdate, this);
20373             store.on("clear", this.refresh, this);
20374             store.on("beforeload", this.onBeforeLoad, this);
20375             store.on("load", this.onLoad, this);
20376             store.on("loadexception", this.onLoad, this);
20377         }
20378         
20379         if(store){
20380             this.refresh();
20381         }
20382     },
20383     /**
20384      * onbeforeLoad - masks the loading area.
20385      *
20386      */
20387     onBeforeLoad : function(store,opts)
20388     {
20389          //Roo.log('onBeforeLoad');   
20390         if (!opts.add) {
20391             this.el.update("");
20392         }
20393         this.el.mask(this.mask ? this.mask : "Loading" ); 
20394     },
20395     onLoad : function ()
20396     {
20397         this.el.unmask();
20398     },
20399     
20400
20401     /**
20402      * Returns the template node the passed child belongs to or null if it doesn't belong to one.
20403      * @param {HTMLElement} node
20404      * @return {HTMLElement} The template node
20405      */
20406     findItemFromChild : function(node){
20407         var el = this.dataName  ?
20408             this.el.child('.roo-tpl-' + this.dataName,true) :
20409             this.el.dom; 
20410         
20411         if(!node || node.parentNode == el){
20412                     return node;
20413             }
20414             var p = node.parentNode;
20415             while(p && p != el){
20416             if(p.parentNode == el){
20417                 return p;
20418             }
20419             p = p.parentNode;
20420         }
20421             return null;
20422     },
20423
20424     /** @ignore */
20425     onClick : function(e){
20426         var item = this.findItemFromChild(e.getTarget());
20427         if(item){
20428             var index = this.indexOf(item);
20429             if(this.onItemClick(item, index, e) !== false){
20430                 this.fireEvent("click", this, index, item, e);
20431             }
20432         }else{
20433             this.clearSelections();
20434         }
20435     },
20436
20437     /** @ignore */
20438     onContextMenu : function(e){
20439         var item = this.findItemFromChild(e.getTarget());
20440         if(item){
20441             this.fireEvent("contextmenu", this, this.indexOf(item), item, e);
20442         }
20443     },
20444
20445     /** @ignore */
20446     onDblClick : function(e){
20447         var item = this.findItemFromChild(e.getTarget());
20448         if(item){
20449             this.fireEvent("dblclick", this, this.indexOf(item), item, e);
20450         }
20451     },
20452
20453     onItemClick : function(item, index, e)
20454     {
20455         if(this.fireEvent("beforeclick", this, index, item, e) === false){
20456             return false;
20457         }
20458         if (this.toggleSelect) {
20459             var m = this.isSelected(item) ? 'unselect' : 'select';
20460             //Roo.log(m);
20461             var _t = this;
20462             _t[m](item, true, false);
20463             return true;
20464         }
20465         if(this.multiSelect || this.singleSelect){
20466             if(this.multiSelect && e.shiftKey && this.lastSelection){
20467                 this.select(this.getNodes(this.indexOf(this.lastSelection), index), false);
20468             }else{
20469                 this.select(item, this.multiSelect && e.ctrlKey);
20470                 this.lastSelection = item;
20471             }
20472             
20473             if(!this.tickable){
20474                 e.preventDefault();
20475             }
20476             
20477         }
20478         return true;
20479     },
20480
20481     /**
20482      * Get the number of selected nodes.
20483      * @return {Number}
20484      */
20485     getSelectionCount : function(){
20486         return this.selections.length;
20487     },
20488
20489     /**
20490      * Get the currently selected nodes.
20491      * @return {Array} An array of HTMLElements
20492      */
20493     getSelectedNodes : function(){
20494         return this.selections;
20495     },
20496
20497     /**
20498      * Get the indexes of the selected nodes.
20499      * @return {Array}
20500      */
20501     getSelectedIndexes : function(){
20502         var indexes = [], s = this.selections;
20503         for(var i = 0, len = s.length; i < len; i++){
20504             indexes.push(s[i].nodeIndex);
20505         }
20506         return indexes;
20507     },
20508
20509     /**
20510      * Clear all selections
20511      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange event
20512      */
20513     clearSelections : function(suppressEvent){
20514         if(this.nodes && (this.multiSelect || this.singleSelect) && this.selections.length > 0){
20515             this.cmp.elements = this.selections;
20516             this.cmp.removeClass(this.selectedClass);
20517             this.selections = [];
20518             if(!suppressEvent){
20519                 this.fireEvent("selectionchange", this, this.selections);
20520             }
20521         }
20522     },
20523
20524     /**
20525      * Returns true if the passed node is selected
20526      * @param {HTMLElement/Number} node The node or node index
20527      * @return {Boolean}
20528      */
20529     isSelected : function(node){
20530         var s = this.selections;
20531         if(s.length < 1){
20532             return false;
20533         }
20534         node = this.getNode(node);
20535         return s.indexOf(node) !== -1;
20536     },
20537
20538     /**
20539      * Selects nodes.
20540      * @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
20541      * @param {Boolean} keepExisting (optional) true to keep existing selections
20542      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
20543      */
20544     select : function(nodeInfo, keepExisting, suppressEvent){
20545         if(nodeInfo instanceof Array){
20546             if(!keepExisting){
20547                 this.clearSelections(true);
20548             }
20549             for(var i = 0, len = nodeInfo.length; i < len; i++){
20550                 this.select(nodeInfo[i], true, true);
20551             }
20552             return;
20553         } 
20554         var node = this.getNode(nodeInfo);
20555         if(!node || this.isSelected(node)){
20556             return; // already selected.
20557         }
20558         if(!keepExisting){
20559             this.clearSelections(true);
20560         }
20561         
20562         if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
20563             Roo.fly(node).addClass(this.selectedClass);
20564             this.selections.push(node);
20565             if(!suppressEvent){
20566                 this.fireEvent("selectionchange", this, this.selections);
20567             }
20568         }
20569         
20570         
20571     },
20572       /**
20573      * Unselects nodes.
20574      * @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
20575      * @param {Boolean} keepExisting (optional) true IGNORED (for campatibility with select)
20576      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
20577      */
20578     unselect : function(nodeInfo, keepExisting, suppressEvent)
20579     {
20580         if(nodeInfo instanceof Array){
20581             Roo.each(this.selections, function(s) {
20582                 this.unselect(s, nodeInfo);
20583             }, this);
20584             return;
20585         }
20586         var node = this.getNode(nodeInfo);
20587         if(!node || !this.isSelected(node)){
20588             //Roo.log("not selected");
20589             return; // not selected.
20590         }
20591         // fireevent???
20592         var ns = [];
20593         Roo.each(this.selections, function(s) {
20594             if (s == node ) {
20595                 Roo.fly(node).removeClass(this.selectedClass);
20596
20597                 return;
20598             }
20599             ns.push(s);
20600         },this);
20601         
20602         this.selections= ns;
20603         this.fireEvent("selectionchange", this, this.selections);
20604     },
20605
20606     /**
20607      * Gets a template node.
20608      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
20609      * @return {HTMLElement} The node or null if it wasn't found
20610      */
20611     getNode : function(nodeInfo){
20612         if(typeof nodeInfo == "string"){
20613             return document.getElementById(nodeInfo);
20614         }else if(typeof nodeInfo == "number"){
20615             return this.nodes[nodeInfo];
20616         }
20617         return nodeInfo;
20618     },
20619
20620     /**
20621      * Gets a range template nodes.
20622      * @param {Number} startIndex
20623      * @param {Number} endIndex
20624      * @return {Array} An array of nodes
20625      */
20626     getNodes : function(start, end){
20627         var ns = this.nodes;
20628         start = start || 0;
20629         end = typeof end == "undefined" ? ns.length - 1 : end;
20630         var nodes = [];
20631         if(start <= end){
20632             for(var i = start; i <= end; i++){
20633                 nodes.push(ns[i]);
20634             }
20635         } else{
20636             for(var i = start; i >= end; i--){
20637                 nodes.push(ns[i]);
20638             }
20639         }
20640         return nodes;
20641     },
20642
20643     /**
20644      * Finds the index of the passed node
20645      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
20646      * @return {Number} The index of the node or -1
20647      */
20648     indexOf : function(node){
20649         node = this.getNode(node);
20650         if(typeof node.nodeIndex == "number"){
20651             return node.nodeIndex;
20652         }
20653         var ns = this.nodes;
20654         for(var i = 0, len = ns.length; i < len; i++){
20655             if(ns[i] == node){
20656                 return i;
20657             }
20658         }
20659         return -1;
20660     }
20661 });
20662 /*
20663  * - LGPL
20664  *
20665  * based on jquery fullcalendar
20666  * 
20667  */
20668
20669 Roo.bootstrap = Roo.bootstrap || {};
20670 /**
20671  * @class Roo.bootstrap.Calendar
20672  * @extends Roo.bootstrap.Component
20673  * Bootstrap Calendar class
20674  * @cfg {Boolean} loadMask (true|false) default false
20675  * @cfg {Object} header generate the user specific header of the calendar, default false
20676
20677  * @constructor
20678  * Create a new Container
20679  * @param {Object} config The config object
20680  */
20681
20682
20683
20684 Roo.bootstrap.Calendar = function(config){
20685     Roo.bootstrap.Calendar.superclass.constructor.call(this, config);
20686      this.addEvents({
20687         /**
20688              * @event select
20689              * Fires when a date is selected
20690              * @param {DatePicker} this
20691              * @param {Date} date The selected date
20692              */
20693         'select': true,
20694         /**
20695              * @event monthchange
20696              * Fires when the displayed month changes 
20697              * @param {DatePicker} this
20698              * @param {Date} date The selected month
20699              */
20700         'monthchange': true,
20701         /**
20702              * @event evententer
20703              * Fires when mouse over an event
20704              * @param {Calendar} this
20705              * @param {event} Event
20706              */
20707         'evententer': true,
20708         /**
20709              * @event eventleave
20710              * Fires when the mouse leaves an
20711              * @param {Calendar} this
20712              * @param {event}
20713              */
20714         'eventleave': true,
20715         /**
20716              * @event eventclick
20717              * Fires when the mouse click an
20718              * @param {Calendar} this
20719              * @param {event}
20720              */
20721         'eventclick': true
20722         
20723     });
20724
20725 };
20726
20727 Roo.extend(Roo.bootstrap.Calendar, Roo.bootstrap.Component,  {
20728     
20729           /**
20730      * @cfg {Roo.data.Store} store
20731      * The data source for the calendar
20732      */
20733         store : false,
20734      /**
20735      * @cfg {Number} startDay
20736      * Day index at which the week should begin, 0-based (defaults to 0, which is Sunday)
20737      */
20738     startDay : 0,
20739     
20740     loadMask : false,
20741     
20742     header : false,
20743       
20744     getAutoCreate : function(){
20745         
20746         
20747         var fc_button = function(name, corner, style, content ) {
20748             return Roo.apply({},{
20749                 tag : 'span',
20750                 cls : 'fc-button fc-button-'+name+' fc-state-default ' + 
20751                          (corner.length ?
20752                             'fc-corner-' + corner.split(' ').join(' fc-corner-') :
20753                             ''
20754                         ),
20755                 html : '<SPAN class="fc-text-'+style+ '">'+content +'</SPAN>',
20756                 unselectable: 'on'
20757             });
20758         };
20759         
20760         var header = {};
20761         
20762         if(!this.header){
20763             header = {
20764                 tag : 'table',
20765                 cls : 'fc-header',
20766                 style : 'width:100%',
20767                 cn : [
20768                     {
20769                         tag: 'tr',
20770                         cn : [
20771                             {
20772                                 tag : 'td',
20773                                 cls : 'fc-header-left',
20774                                 cn : [
20775                                     fc_button('prev', 'left', 'arrow', '&#8249;' ),
20776                                     fc_button('next', 'right', 'arrow', '&#8250;' ),
20777                                     { tag: 'span', cls: 'fc-header-space' },
20778                                     fc_button('today', 'left right', '', 'today' )  // neds state disabled..
20779
20780
20781                                 ]
20782                             },
20783
20784                             {
20785                                 tag : 'td',
20786                                 cls : 'fc-header-center',
20787                                 cn : [
20788                                     {
20789                                         tag: 'span',
20790                                         cls: 'fc-header-title',
20791                                         cn : {
20792                                             tag: 'H2',
20793                                             html : 'month / year'
20794                                         }
20795                                     }
20796
20797                                 ]
20798                             },
20799                             {
20800                                 tag : 'td',
20801                                 cls : 'fc-header-right',
20802                                 cn : [
20803                               /*      fc_button('month', 'left', '', 'month' ),
20804                                     fc_button('week', '', '', 'week' ),
20805                                     fc_button('day', 'right', '', 'day' )
20806                                 */    
20807
20808                                 ]
20809                             }
20810
20811                         ]
20812                     }
20813                 ]
20814             };
20815         }
20816         
20817         header = this.header;
20818         
20819        
20820         var cal_heads = function() {
20821             var ret = [];
20822             // fixme - handle this.
20823             
20824             for (var i =0; i < Date.dayNames.length; i++) {
20825                 var d = Date.dayNames[i];
20826                 ret.push({
20827                     tag: 'th',
20828                     cls : 'fc-day-header fc-' + d.substring(0,3).toLowerCase() + ' fc-widget-header',
20829                     html : d.substring(0,3)
20830                 });
20831                 
20832             }
20833             ret[0].cls += ' fc-first';
20834             ret[6].cls += ' fc-last';
20835             return ret;
20836         };
20837         var cal_cell = function(n) {
20838             return  {
20839                 tag: 'td',
20840                 cls : 'fc-day fc-'+n + ' fc-widget-content', ///fc-other-month fc-past
20841                 cn : [
20842                     {
20843                         cn : [
20844                             {
20845                                 cls: 'fc-day-number',
20846                                 html: 'D'
20847                             },
20848                             {
20849                                 cls: 'fc-day-content',
20850                              
20851                                 cn : [
20852                                      {
20853                                         style: 'position: relative;' // height: 17px;
20854                                     }
20855                                 ]
20856                             }
20857                             
20858                             
20859                         ]
20860                     }
20861                 ]
20862                 
20863             }
20864         };
20865         var cal_rows = function() {
20866             
20867             var ret = [];
20868             for (var r = 0; r < 6; r++) {
20869                 var row= {
20870                     tag : 'tr',
20871                     cls : 'fc-week',
20872                     cn : []
20873                 };
20874                 
20875                 for (var i =0; i < Date.dayNames.length; i++) {
20876                     var d = Date.dayNames[i];
20877                     row.cn.push(cal_cell(d.substring(0,3).toLowerCase()));
20878
20879                 }
20880                 row.cn[0].cls+=' fc-first';
20881                 row.cn[0].cn[0].style = 'min-height:90px';
20882                 row.cn[6].cls+=' fc-last';
20883                 ret.push(row);
20884                 
20885             }
20886             ret[0].cls += ' fc-first';
20887             ret[4].cls += ' fc-prev-last';
20888             ret[5].cls += ' fc-last';
20889             return ret;
20890             
20891         };
20892         
20893         var cal_table = {
20894             tag: 'table',
20895             cls: 'fc-border-separate',
20896             style : 'width:100%',
20897             cellspacing  : 0,
20898             cn : [
20899                 { 
20900                     tag: 'thead',
20901                     cn : [
20902                         { 
20903                             tag: 'tr',
20904                             cls : 'fc-first fc-last',
20905                             cn : cal_heads()
20906                         }
20907                     ]
20908                 },
20909                 { 
20910                     tag: 'tbody',
20911                     cn : cal_rows()
20912                 }
20913                   
20914             ]
20915         };
20916          
20917          var cfg = {
20918             cls : 'fc fc-ltr',
20919             cn : [
20920                 header,
20921                 {
20922                     cls : 'fc-content',
20923                     style : "position: relative;",
20924                     cn : [
20925                         {
20926                             cls : 'fc-view fc-view-month fc-grid',
20927                             style : 'position: relative',
20928                             unselectable : 'on',
20929                             cn : [
20930                                 {
20931                                     cls : 'fc-event-container',
20932                                     style : 'position:absolute;z-index:8;top:0;left:0;'
20933                                 },
20934                                 cal_table
20935                             ]
20936                         }
20937                     ]
20938     
20939                 }
20940            ] 
20941             
20942         };
20943         
20944          
20945         
20946         return cfg;
20947     },
20948     
20949     
20950     initEvents : function()
20951     {
20952         if(!this.store){
20953             throw "can not find store for calendar";
20954         }
20955         
20956         var mark = {
20957             tag: "div",
20958             cls:"x-dlg-mask",
20959             style: "text-align:center",
20960             cn: [
20961                 {
20962                     tag: "div",
20963                     style: "background-color:white;width:50%;margin:250 auto",
20964                     cn: [
20965                         {
20966                             tag: "img",
20967                             src: Roo.rootURL + '/images/ux/lightbox/loading.gif' 
20968                         },
20969                         {
20970                             tag: "span",
20971                             html: "Loading"
20972                         }
20973                         
20974                     ]
20975                 }
20976             ]
20977         };
20978         this.maskEl = Roo.DomHelper.append(this.el.select('.fc-content', true).first(), mark, true);
20979         
20980         var size = this.el.select('.fc-content', true).first().getSize();
20981         this.maskEl.setSize(size.width, size.height);
20982         this.maskEl.enableDisplayMode("block");
20983         if(!this.loadMask){
20984             this.maskEl.hide();
20985         }
20986         
20987         this.store = Roo.factory(this.store, Roo.data);
20988         this.store.on('load', this.onLoad, this);
20989         this.store.on('beforeload', this.onBeforeLoad, this);
20990         
20991         this.resize();
20992         
20993         this.cells = this.el.select('.fc-day',true);
20994         //Roo.log(this.cells);
20995         this.textNodes = this.el.query('.fc-day-number');
20996         this.cells.addClassOnOver('fc-state-hover');
20997         
20998         this.el.select('.fc-button-prev',true).on('click', this.showPrevMonth, this);
20999         this.el.select('.fc-button-next',true).on('click', this.showNextMonth, this);
21000         this.el.select('.fc-button-today',true).on('click', this.showToday, this);
21001         this.el.select('.fc-button',true).addClassOnOver('fc-state-hover');
21002         
21003         this.on('monthchange', this.onMonthChange, this);
21004         
21005         this.update(new Date().clearTime());
21006     },
21007     
21008     resize : function() {
21009         var sz  = this.el.getSize();
21010         
21011         this.el.select('.fc-day-header',true).setWidth(sz.width / 7);
21012         this.el.select('.fc-day-content div',true).setHeight(34);
21013     },
21014     
21015     
21016     // private
21017     showPrevMonth : function(e){
21018         this.update(this.activeDate.add("mo", -1));
21019     },
21020     showToday : function(e){
21021         this.update(new Date().clearTime());
21022     },
21023     // private
21024     showNextMonth : function(e){
21025         this.update(this.activeDate.add("mo", 1));
21026     },
21027
21028     // private
21029     showPrevYear : function(){
21030         this.update(this.activeDate.add("y", -1));
21031     },
21032
21033     // private
21034     showNextYear : function(){
21035         this.update(this.activeDate.add("y", 1));
21036     },
21037
21038     
21039    // private
21040     update : function(date)
21041     {
21042         var vd = this.activeDate;
21043         this.activeDate = date;
21044 //        if(vd && this.el){
21045 //            var t = date.getTime();
21046 //            if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
21047 //                Roo.log('using add remove');
21048 //                
21049 //                this.fireEvent('monthchange', this, date);
21050 //                
21051 //                this.cells.removeClass("fc-state-highlight");
21052 //                this.cells.each(function(c){
21053 //                   if(c.dateValue == t){
21054 //                       c.addClass("fc-state-highlight");
21055 //                       setTimeout(function(){
21056 //                            try{c.dom.firstChild.focus();}catch(e){}
21057 //                       }, 50);
21058 //                       return false;
21059 //                   }
21060 //                   return true;
21061 //                });
21062 //                return;
21063 //            }
21064 //        }
21065         
21066         var days = date.getDaysInMonth();
21067         
21068         var firstOfMonth = date.getFirstDateOfMonth();
21069         var startingPos = firstOfMonth.getDay()-this.startDay;
21070         
21071         if(startingPos < this.startDay){
21072             startingPos += 7;
21073         }
21074         
21075         var pm = date.add(Date.MONTH, -1);
21076         var prevStart = pm.getDaysInMonth()-startingPos;
21077 //        
21078         this.cells = this.el.select('.fc-day',true);
21079         this.textNodes = this.el.query('.fc-day-number');
21080         this.cells.addClassOnOver('fc-state-hover');
21081         
21082         var cells = this.cells.elements;
21083         var textEls = this.textNodes;
21084         
21085         Roo.each(cells, function(cell){
21086             cell.removeClass([ 'fc-past', 'fc-other-month', 'fc-future', 'fc-state-highlight', 'fc-state-disabled']);
21087         });
21088         
21089         days += startingPos;
21090
21091         // convert everything to numbers so it's fast
21092         var day = 86400000;
21093         var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
21094         //Roo.log(d);
21095         //Roo.log(pm);
21096         //Roo.log(prevStart);
21097         
21098         var today = new Date().clearTime().getTime();
21099         var sel = date.clearTime().getTime();
21100         var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
21101         var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
21102         var ddMatch = this.disabledDatesRE;
21103         var ddText = this.disabledDatesText;
21104         var ddays = this.disabledDays ? this.disabledDays.join("") : false;
21105         var ddaysText = this.disabledDaysText;
21106         var format = this.format;
21107         
21108         var setCellClass = function(cal, cell){
21109             cell.row = 0;
21110             cell.events = [];
21111             cell.more = [];
21112             //Roo.log('set Cell Class');
21113             cell.title = "";
21114             var t = d.getTime();
21115             
21116             //Roo.log(d);
21117             
21118             cell.dateValue = t;
21119             if(t == today){
21120                 cell.className += " fc-today";
21121                 cell.className += " fc-state-highlight";
21122                 cell.title = cal.todayText;
21123             }
21124             if(t == sel){
21125                 // disable highlight in other month..
21126                 //cell.className += " fc-state-highlight";
21127                 
21128             }
21129             // disabling
21130             if(t < min) {
21131                 cell.className = " fc-state-disabled";
21132                 cell.title = cal.minText;
21133                 return;
21134             }
21135             if(t > max) {
21136                 cell.className = " fc-state-disabled";
21137                 cell.title = cal.maxText;
21138                 return;
21139             }
21140             if(ddays){
21141                 if(ddays.indexOf(d.getDay()) != -1){
21142                     cell.title = ddaysText;
21143                     cell.className = " fc-state-disabled";
21144                 }
21145             }
21146             if(ddMatch && format){
21147                 var fvalue = d.dateFormat(format);
21148                 if(ddMatch.test(fvalue)){
21149                     cell.title = ddText.replace("%0", fvalue);
21150                     cell.className = " fc-state-disabled";
21151                 }
21152             }
21153             
21154             if (!cell.initialClassName) {
21155                 cell.initialClassName = cell.dom.className;
21156             }
21157             
21158             cell.dom.className = cell.initialClassName  + ' ' +  cell.className;
21159         };
21160
21161         var i = 0;
21162         
21163         for(; i < startingPos; i++) {
21164             textEls[i].innerHTML = (++prevStart);
21165             d.setDate(d.getDate()+1);
21166             
21167             cells[i].className = "fc-past fc-other-month";
21168             setCellClass(this, cells[i]);
21169         }
21170         
21171         var intDay = 0;
21172         
21173         for(; i < days; i++){
21174             intDay = i - startingPos + 1;
21175             textEls[i].innerHTML = (intDay);
21176             d.setDate(d.getDate()+1);
21177             
21178             cells[i].className = ''; // "x-date-active";
21179             setCellClass(this, cells[i]);
21180         }
21181         var extraDays = 0;
21182         
21183         for(; i < 42; i++) {
21184             textEls[i].innerHTML = (++extraDays);
21185             d.setDate(d.getDate()+1);
21186             
21187             cells[i].className = "fc-future fc-other-month";
21188             setCellClass(this, cells[i]);
21189         }
21190         
21191         this.el.select('.fc-header-title h2',true).update(Date.monthNames[date.getMonth()] + " " + date.getFullYear());
21192         
21193         var totalRows = Math.ceil((date.getDaysInMonth() + date.getFirstDateOfMonth().getDay()) / 7);
21194         
21195         this.el.select('tr.fc-week.fc-prev-last',true).removeClass('fc-last');
21196         this.el.select('tr.fc-week.fc-next-last',true).addClass('fc-last').show();
21197         
21198         if(totalRows != 6){
21199             this.el.select('tr.fc-week.fc-last',true).removeClass('fc-last').addClass('fc-next-last').hide();
21200             this.el.select('tr.fc-week.fc-prev-last',true).addClass('fc-last');
21201         }
21202         
21203         this.fireEvent('monthchange', this, date);
21204         
21205         
21206         /*
21207         if(!this.internalRender){
21208             var main = this.el.dom.firstChild;
21209             var w = main.offsetWidth;
21210             this.el.setWidth(w + this.el.getBorderWidth("lr"));
21211             Roo.fly(main).setWidth(w);
21212             this.internalRender = true;
21213             // opera does not respect the auto grow header center column
21214             // then, after it gets a width opera refuses to recalculate
21215             // without a second pass
21216             if(Roo.isOpera && !this.secondPass){
21217                 main.rows[0].cells[1].style.width = (w - (main.rows[0].cells[0].offsetWidth+main.rows[0].cells[2].offsetWidth)) + "px";
21218                 this.secondPass = true;
21219                 this.update.defer(10, this, [date]);
21220             }
21221         }
21222         */
21223         
21224     },
21225     
21226     findCell : function(dt) {
21227         dt = dt.clearTime().getTime();
21228         var ret = false;
21229         this.cells.each(function(c){
21230             //Roo.log("check " +c.dateValue + '?=' + dt);
21231             if(c.dateValue == dt){
21232                 ret = c;
21233                 return false;
21234             }
21235             return true;
21236         });
21237         
21238         return ret;
21239     },
21240     
21241     findCells : function(ev) {
21242         var s = ev.start.clone().clearTime().getTime();
21243        // Roo.log(s);
21244         var e= ev.end.clone().clearTime().getTime();
21245        // Roo.log(e);
21246         var ret = [];
21247         this.cells.each(function(c){
21248              ////Roo.log("check " +c.dateValue + '<' + e + ' > ' + s);
21249             
21250             if(c.dateValue > e){
21251                 return ;
21252             }
21253             if(c.dateValue < s){
21254                 return ;
21255             }
21256             ret.push(c);
21257         });
21258         
21259         return ret;    
21260     },
21261     
21262 //    findBestRow: function(cells)
21263 //    {
21264 //        var ret = 0;
21265 //        
21266 //        for (var i =0 ; i < cells.length;i++) {
21267 //            ret  = Math.max(cells[i].rows || 0,ret);
21268 //        }
21269 //        return ret;
21270 //        
21271 //    },
21272     
21273     
21274     addItem : function(ev)
21275     {
21276         // look for vertical location slot in
21277         var cells = this.findCells(ev);
21278         
21279 //        ev.row = this.findBestRow(cells);
21280         
21281         // work out the location.
21282         
21283         var crow = false;
21284         var rows = [];
21285         for(var i =0; i < cells.length; i++) {
21286             
21287             cells[i].row = cells[0].row;
21288             
21289             if(i == 0){
21290                 cells[i].row = cells[i].row + 1;
21291             }
21292             
21293             if (!crow) {
21294                 crow = {
21295                     start : cells[i],
21296                     end :  cells[i]
21297                 };
21298                 continue;
21299             }
21300             if (crow.start.getY() == cells[i].getY()) {
21301                 // on same row.
21302                 crow.end = cells[i];
21303                 continue;
21304             }
21305             // different row.
21306             rows.push(crow);
21307             crow = {
21308                 start: cells[i],
21309                 end : cells[i]
21310             };
21311             
21312         }
21313         
21314         rows.push(crow);
21315         ev.els = [];
21316         ev.rows = rows;
21317         ev.cells = cells;
21318         
21319         cells[0].events.push(ev);
21320         
21321         this.calevents.push(ev);
21322     },
21323     
21324     clearEvents: function() {
21325         
21326         if(!this.calevents){
21327             return;
21328         }
21329         
21330         Roo.each(this.cells.elements, function(c){
21331             c.row = 0;
21332             c.events = [];
21333             c.more = [];
21334         });
21335         
21336         Roo.each(this.calevents, function(e) {
21337             Roo.each(e.els, function(el) {
21338                 el.un('mouseenter' ,this.onEventEnter, this);
21339                 el.un('mouseleave' ,this.onEventLeave, this);
21340                 el.remove();
21341             },this);
21342         },this);
21343         
21344         Roo.each(Roo.select('.fc-more-event', true).elements, function(e){
21345             e.remove();
21346         });
21347         
21348     },
21349     
21350     renderEvents: function()
21351     {   
21352         var _this = this;
21353         
21354         this.cells.each(function(c) {
21355             
21356             if(c.row < 5){
21357                 return;
21358             }
21359             
21360             var ev = c.events;
21361             
21362             var r = 4;
21363             if(c.row != c.events.length){
21364                 r = 4 - (4 - (c.row - c.events.length));
21365             }
21366             
21367             c.events = ev.slice(0, r);
21368             c.more = ev.slice(r);
21369             
21370             if(c.more.length && c.more.length == 1){
21371                 c.events.push(c.more.pop());
21372             }
21373             
21374             c.row = (c.row - ev.length) + c.events.length + ((c.more.length) ? 1 : 0);
21375             
21376         });
21377             
21378         this.cells.each(function(c) {
21379             
21380             c.select('.fc-day-content div',true).first().setHeight(Math.max(34, c.row * 20));
21381             
21382             
21383             for (var e = 0; e < c.events.length; e++){
21384                 var ev = c.events[e];
21385                 var rows = ev.rows;
21386                 
21387                 for(var i = 0; i < rows.length; i++) {
21388                 
21389                     // how many rows should it span..
21390
21391                     var  cfg = {
21392                         cls : 'roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable',
21393                         style : 'position: absolute', // left: 387px; width: 121px; top: 359px;
21394
21395                         unselectable : "on",
21396                         cn : [
21397                             {
21398                                 cls: 'fc-event-inner',
21399                                 cn : [
21400     //                                {
21401     //                                  tag:'span',
21402     //                                  cls: 'fc-event-time',
21403     //                                  html : cells.length > 1 ? '' : ev.time
21404     //                                },
21405                                     {
21406                                       tag:'span',
21407                                       cls: 'fc-event-title',
21408                                       html : String.format('{0}', ev.title)
21409                                     }
21410
21411
21412                                 ]
21413                             },
21414                             {
21415                                 cls: 'ui-resizable-handle ui-resizable-e',
21416                                 html : '&nbsp;&nbsp;&nbsp'
21417                             }
21418
21419                         ]
21420                     };
21421
21422                     if (i == 0) {
21423                         cfg.cls += ' fc-event-start';
21424                     }
21425                     if ((i+1) == rows.length) {
21426                         cfg.cls += ' fc-event-end';
21427                     }
21428
21429                     var ctr = _this.el.select('.fc-event-container',true).first();
21430                     var cg = ctr.createChild(cfg);
21431
21432                     var sbox = rows[i].start.select('.fc-day-content',true).first().getBox();
21433                     var ebox = rows[i].end.select('.fc-day-content',true).first().getBox();
21434
21435                     var r = (c.more.length) ? 1 : 0;
21436                     cg.setXY([sbox.x +2, sbox.y + ((c.row - c.events.length - r + e) * 20)]);    
21437                     cg.setWidth(ebox.right - sbox.x -2);
21438
21439                     cg.on('mouseenter' ,_this.onEventEnter, _this, ev);
21440                     cg.on('mouseleave' ,_this.onEventLeave, _this, ev);
21441                     cg.on('click', _this.onEventClick, _this, ev);
21442
21443                     ev.els.push(cg);
21444                     
21445                 }
21446                 
21447             }
21448             
21449             
21450             if(c.more.length){
21451                 var  cfg = {
21452                     cls : 'fc-more-event roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable fc-event-start fc-event-end',
21453                     style : 'position: absolute',
21454                     unselectable : "on",
21455                     cn : [
21456                         {
21457                             cls: 'fc-event-inner',
21458                             cn : [
21459                                 {
21460                                   tag:'span',
21461                                   cls: 'fc-event-title',
21462                                   html : 'More'
21463                                 }
21464
21465
21466                             ]
21467                         },
21468                         {
21469                             cls: 'ui-resizable-handle ui-resizable-e',
21470                             html : '&nbsp;&nbsp;&nbsp'
21471                         }
21472
21473                     ]
21474                 };
21475
21476                 var ctr = _this.el.select('.fc-event-container',true).first();
21477                 var cg = ctr.createChild(cfg);
21478
21479                 var sbox = c.select('.fc-day-content',true).first().getBox();
21480                 var ebox = c.select('.fc-day-content',true).first().getBox();
21481                 //Roo.log(cg);
21482                 cg.setXY([sbox.x +2, sbox.y +((c.row - 1) * 20)]);    
21483                 cg.setWidth(ebox.right - sbox.x -2);
21484
21485                 cg.on('click', _this.onMoreEventClick, _this, c.more);
21486                 
21487             }
21488             
21489         });
21490         
21491         
21492         
21493     },
21494     
21495     onEventEnter: function (e, el,event,d) {
21496         this.fireEvent('evententer', this, el, event);
21497     },
21498     
21499     onEventLeave: function (e, el,event,d) {
21500         this.fireEvent('eventleave', this, el, event);
21501     },
21502     
21503     onEventClick: function (e, el,event,d) {
21504         this.fireEvent('eventclick', this, el, event);
21505     },
21506     
21507     onMonthChange: function () {
21508         this.store.load();
21509     },
21510     
21511     onMoreEventClick: function(e, el, more)
21512     {
21513         var _this = this;
21514         
21515         this.calpopover.placement = 'right';
21516         this.calpopover.setTitle('More');
21517         
21518         this.calpopover.setContent('');
21519         
21520         var ctr = this.calpopover.el.select('.popover-content', true).first();
21521         
21522         Roo.each(more, function(m){
21523             var cfg = {
21524                 cls : 'fc-event-hori fc-event-draggable',
21525                 html : m.title
21526             };
21527             var cg = ctr.createChild(cfg);
21528             
21529             cg.on('click', _this.onEventClick, _this, m);
21530         });
21531         
21532         this.calpopover.show(el);
21533         
21534         
21535     },
21536     
21537     onLoad: function () 
21538     {   
21539         this.calevents = [];
21540         var cal = this;
21541         
21542         if(this.store.getCount() > 0){
21543             this.store.data.each(function(d){
21544                cal.addItem({
21545                     id : d.data.id,
21546                     start: (typeof(d.data.start_dt) === 'string') ? new Date.parseDate(d.data.start_dt, 'Y-m-d H:i:s') : d.data.start_dt,
21547                     end : (typeof(d.data.end_dt) === 'string') ? new Date.parseDate(d.data.end_dt, 'Y-m-d H:i:s') : d.data.end_dt,
21548                     time : d.data.start_time,
21549                     title : d.data.title,
21550                     description : d.data.description,
21551                     venue : d.data.venue
21552                 });
21553             });
21554         }
21555         
21556         this.renderEvents();
21557         
21558         if(this.calevents.length && this.loadMask){
21559             this.maskEl.hide();
21560         }
21561     },
21562     
21563     onBeforeLoad: function()
21564     {
21565         this.clearEvents();
21566         if(this.loadMask){
21567             this.maskEl.show();
21568         }
21569     }
21570 });
21571
21572  
21573  /*
21574  * - LGPL
21575  *
21576  * element
21577  * 
21578  */
21579
21580 /**
21581  * @class Roo.bootstrap.Popover
21582  * @extends Roo.bootstrap.Component
21583  * @parent none builder
21584  * @children Roo.bootstrap.Component
21585  * Bootstrap Popover class
21586  * @cfg {String} html contents of the popover   (or false to use children..)
21587  * @cfg {String} title of popover (or false to hide)
21588  * @cfg {String|function} (right|top|bottom|left|auto) placement how it is placed
21589  * @cfg {String} trigger click || hover (or false to trigger manually)
21590  * @cfg {Boolean} modal - popovers that are modal will mask the screen, and must be closed with another event.
21591  * @cfg {String|Boolean|Roo.Element} add click hander to trigger show over what element
21592  *      - if false and it has a 'parent' then it will be automatically added to that element
21593  *      - if string - Roo.get  will be called 
21594  * @cfg {Number} delay - delay before showing
21595  
21596  * @constructor
21597  * Create a new Popover
21598  * @param {Object} config The config object
21599  */
21600
21601 Roo.bootstrap.Popover = function(config){
21602     Roo.bootstrap.Popover.superclass.constructor.call(this, config);
21603     
21604     this.addEvents({
21605         // raw events
21606          /**
21607          * @event show
21608          * After the popover show
21609          * 
21610          * @param {Roo.bootstrap.Popover} this
21611          */
21612         "show" : true,
21613         /**
21614          * @event hide
21615          * After the popover hide
21616          * 
21617          * @param {Roo.bootstrap.Popover} this
21618          */
21619         "hide" : true
21620     });
21621 };
21622
21623 Roo.extend(Roo.bootstrap.Popover, Roo.bootstrap.Component,  {
21624     
21625     title: false,
21626     html: false,
21627     
21628     placement : 'right',
21629     trigger : 'hover', // hover
21630     modal : false,
21631     delay : 0,
21632     
21633     over: false,
21634     
21635     can_build_overlaid : false,
21636     
21637     maskEl : false, // the mask element
21638     headerEl : false,
21639     contentEl : false,
21640     alignEl : false, // when show is called with an element - this get's stored.
21641     
21642     getChildContainer : function()
21643     {
21644         return this.contentEl;
21645         
21646     },
21647     getPopoverHeader : function()
21648     {
21649         this.title = true; // flag not to hide it..
21650         this.headerEl.addClass('p-0');
21651         return this.headerEl
21652     },
21653     
21654     
21655     getAutoCreate : function(){
21656          
21657         var cfg = {
21658            cls : 'popover roo-dynamic shadow roo-popover' + (this.modal ? '-modal' : ''),
21659            style: 'display:block',
21660            cn : [
21661                 {
21662                     cls : 'arrow'
21663                 },
21664                 {
21665                     cls : 'popover-inner ',
21666                     cn : [
21667                         {
21668                             tag: 'h3',
21669                             cls: 'popover-title popover-header',
21670                             html : this.title === false ? '' : this.title
21671                         },
21672                         {
21673                             cls : 'popover-content popover-body '  + (this.cls || ''),
21674                             html : this.html || ''
21675                         }
21676                     ]
21677                     
21678                 }
21679            ]
21680         };
21681         
21682         return cfg;
21683     },
21684     /**
21685      * @param {string} the title
21686      */
21687     setTitle: function(str)
21688     {
21689         this.title = str;
21690         if (this.el) {
21691             this.headerEl.dom.innerHTML = str;
21692         }
21693         
21694     },
21695     /**
21696      * @param {string} the body content
21697      */
21698     setContent: function(str)
21699     {
21700         this.html = str;
21701         if (this.contentEl) {
21702             this.contentEl.dom.innerHTML = str;
21703         }
21704         
21705     },
21706     // as it get's added to the bottom of the page.
21707     onRender : function(ct, position)
21708     {
21709         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
21710         
21711         
21712         
21713         if(!this.el){
21714             var cfg = Roo.apply({},  this.getAutoCreate());
21715             cfg.id = Roo.id();
21716             
21717             if (this.cls) {
21718                 cfg.cls += ' ' + this.cls;
21719             }
21720             if (this.style) {
21721                 cfg.style = this.style;
21722             }
21723             //Roo.log("adding to ");
21724             this.el = Roo.get(document.body).createChild(cfg, position);
21725 //            Roo.log(this.el);
21726         }
21727         
21728         this.contentEl = this.el.select('.popover-content',true).first();
21729         this.headerEl =  this.el.select('.popover-title',true).first();
21730         
21731         var nitems = [];
21732         if(typeof(this.items) != 'undefined'){
21733             var items = this.items;
21734             delete this.items;
21735
21736             for(var i =0;i < items.length;i++) {
21737                 nitems.push(this.addxtype(Roo.apply({}, items[i])));
21738             }
21739         }
21740
21741         this.items = nitems;
21742         
21743         this.maskEl = Roo.DomHelper.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
21744         Roo.EventManager.onWindowResize(this.resizeMask, this, true);
21745         
21746         
21747         
21748         this.initEvents();
21749     },
21750     
21751     resizeMask : function()
21752     {
21753         this.maskEl.setSize(
21754             Roo.lib.Dom.getViewWidth(true),
21755             Roo.lib.Dom.getViewHeight(true)
21756         );
21757     },
21758     
21759     initEvents : function()
21760     {
21761         
21762         if (!this.modal) { 
21763             Roo.bootstrap.Popover.register(this);
21764         }
21765          
21766         this.arrowEl = this.el.select('.arrow',true).first();
21767         this.headerEl.setVisibilityMode(Roo.Element.DISPLAY); // probably not needed as it's default in BS4
21768         this.el.enableDisplayMode('block');
21769         this.el.hide();
21770  
21771         
21772         if (this.over === false && !this.parent()) {
21773             return; 
21774         }
21775         if (this.triggers === false) {
21776             return;
21777         }
21778          
21779         // support parent
21780         var on_el = (this.over == 'parent' || this.over === false) ? this.parent().el : Roo.get(this.over);
21781         var triggers = this.trigger ? this.trigger.split(' ') : [];
21782         Roo.each(triggers, function(trigger) {
21783         
21784             if (trigger == 'click') {
21785                 on_el.on('click', this.toggle, this);
21786             } else if (trigger != 'manual') {
21787                 var eventIn  = trigger == 'hover' ? 'mouseenter' : 'focusin';
21788                 var eventOut = trigger == 'hover' ? 'mouseleave' : 'focusout';
21789       
21790                 on_el.on(eventIn  ,this.enter, this);
21791                 on_el.on(eventOut, this.leave, this);
21792             }
21793         }, this);
21794     },
21795     
21796     
21797     // private
21798     timeout : null,
21799     hoverState : null,
21800     
21801     toggle : function () {
21802         this.hoverState == 'in' ? this.leave() : this.enter();
21803     },
21804     
21805     enter : function () {
21806         
21807         clearTimeout(this.timeout);
21808     
21809         this.hoverState = 'in';
21810     
21811         if (!this.delay || !this.delay.show) {
21812             this.show();
21813             return;
21814         }
21815         var _t = this;
21816         this.timeout = setTimeout(function () {
21817             if (_t.hoverState == 'in') {
21818                 _t.show();
21819             }
21820         }, this.delay.show)
21821     },
21822     
21823     leave : function() {
21824         clearTimeout(this.timeout);
21825     
21826         this.hoverState = 'out';
21827     
21828         if (!this.delay || !this.delay.hide) {
21829             this.hide();
21830             return;
21831         }
21832         var _t = this;
21833         this.timeout = setTimeout(function () {
21834             if (_t.hoverState == 'out') {
21835                 _t.hide();
21836             }
21837         }, this.delay.hide)
21838     },
21839     
21840     /**
21841      * update the position of the dialog
21842      * normally this is needed if the popover get's bigger - due to a Table reload etc..
21843      * 
21844      *
21845      */
21846     
21847     doAlign : function()
21848     {
21849         
21850         if (this.alignEl) {
21851             this.updatePosition(this.placement, true);
21852              
21853         } else {
21854             // this is usually just done by the builder = to show the popoup in the middle of the scren.
21855             var es = this.el.getSize();
21856             var x = Roo.lib.Dom.getViewWidth()/2;
21857             var y = Roo.lib.Dom.getViewHeight()/2;
21858             this.el.setXY([ x-(es.width/2),  y-(es.height/2)] );
21859             
21860         }
21861
21862          
21863          
21864         
21865         
21866     },
21867     
21868     /**
21869      * Show the popover
21870      * @param {Roo.Element|string|Boolean} - element to align and point to. (set align to [ pos, offset ])
21871      * @param {string} (left|right|top|bottom) position
21872      */
21873     show : function (on_el, placement)
21874     {
21875         this.placement = typeof(placement) == 'undefined' ?  this.placement   : placement;
21876         on_el = on_el || false; // default to false
21877          
21878         if (!on_el) {
21879             if (this.parent() && (this.over == 'parent' || (this.over === false))) {
21880                 on_el = this.parent().el;
21881             } else if (this.over) {
21882                 on_el = Roo.get(this.over);
21883             }
21884             
21885         }
21886         
21887         this.alignEl = Roo.get( on_el );
21888
21889         if (!this.el) {
21890             this.render(document.body);
21891         }
21892         
21893         
21894          
21895         
21896         if (this.title === false) {
21897             this.headerEl.hide();
21898         }
21899         
21900        
21901         this.el.show();
21902         this.el.dom.style.display = 'block';
21903          
21904         this.doAlign();
21905         
21906         //var arrow = this.el.select('.arrow',true).first();
21907         //arrow.set(align[2], 
21908         
21909         this.el.addClass('in');
21910         
21911          
21912         
21913         this.hoverState = 'in';
21914         
21915         if (this.modal) {
21916             this.maskEl.setSize(Roo.lib.Dom.getViewWidth(true),   Roo.lib.Dom.getViewHeight(true));
21917             this.maskEl.setStyle('z-index', Roo.bootstrap.Popover.zIndex++);
21918             this.maskEl.dom.style.display = 'block';
21919             this.maskEl.addClass('show');
21920         }
21921         this.el.setStyle('z-index', Roo.bootstrap.Popover.zIndex++);
21922  
21923         this.fireEvent('show', this);
21924         
21925     },
21926     /**
21927      * fire this manually after loading a grid in the table for example
21928      * @param {string} (left|right|top|bottom) where to try and put it (use false to use the last one)
21929      * @param {Boolean} try and move it if we cant get right position.
21930      */
21931     updatePosition : function(placement, try_move)
21932     {
21933         // allow for calling with no parameters
21934         placement = placement   ? placement :  this.placement;
21935         try_move = typeof(try_move) == 'undefined' ? true : try_move;
21936         
21937         this.el.removeClass([
21938             'fade','top','bottom', 'left', 'right','in',
21939             'bs-popover-top','bs-popover-bottom', 'bs-popover-left', 'bs-popover-right'
21940         ]);
21941         this.el.addClass(placement + ' bs-popover-' + placement);
21942         
21943         if (!this.alignEl ) {
21944             return false;
21945         }
21946         
21947         switch (placement) {
21948             case 'right':
21949                 var exact = this.el.getAlignToXY(this.alignEl, 'tl-tr', [10,0]);
21950                 var offset = this.el.getAlignToXY(this.alignEl, 'tl-tr?',[10,0]);
21951                 if (!try_move || exact.equals(offset) || exact[0] == offset[0] ) {
21952                     //normal display... or moved up/down.
21953                     this.el.setXY(offset);
21954                     var xy = this.alignEl.getAnchorXY('tr', false);
21955                     xy[0]+=2;xy[1]+=5;
21956                     this.arrowEl.setXY(xy);
21957                     return true;
21958                 }
21959                 // continue through...
21960                 return this.updatePosition('left', false);
21961                 
21962             
21963             case 'left':
21964                 var exact = this.el.getAlignToXY(this.alignEl, 'tr-tl', [-10,0]);
21965                 var offset = this.el.getAlignToXY(this.alignEl, 'tr-tl?',[-10,0]);
21966                 if (!try_move || exact.equals(offset) || exact[0] == offset[0] ) {
21967                     //normal display... or moved up/down.
21968                     this.el.setXY(offset);
21969                     var xy = this.alignEl.getAnchorXY('tl', false);
21970                     xy[0]-=10;xy[1]+=5; // << fix me
21971                     this.arrowEl.setXY(xy);
21972                     return true;
21973                 }
21974                 // call self...
21975                 return this.updatePosition('right', false);
21976             
21977             case 'top':
21978                 var exact = this.el.getAlignToXY(this.alignEl, 'b-t', [0,-10]);
21979                 var offset = this.el.getAlignToXY(this.alignEl, 'b-t?',[0,-10]);
21980                 if (!try_move || exact.equals(offset) || exact[1] == offset[1] ) {
21981                     //normal display... or moved up/down.
21982                     this.el.setXY(offset);
21983                     var xy = this.alignEl.getAnchorXY('t', false);
21984                     xy[1]-=10; // << fix me
21985                     this.arrowEl.setXY(xy);
21986                     return true;
21987                 }
21988                 // fall through
21989                return this.updatePosition('bottom', false);
21990             
21991             case 'bottom':
21992                  var exact = this.el.getAlignToXY(this.alignEl, 't-b', [0,10]);
21993                 var offset = this.el.getAlignToXY(this.alignEl, 't-b?',[0,10]);
21994                 if (!try_move || exact.equals(offset) || exact[1] == offset[1] ) {
21995                     //normal display... or moved up/down.
21996                     this.el.setXY(offset);
21997                     var xy = this.alignEl.getAnchorXY('b', false);
21998                      xy[1]+=2; // << fix me
21999                     this.arrowEl.setXY(xy);
22000                     return true;
22001                 }
22002                 // fall through
22003                 return this.updatePosition('top', false);
22004                 
22005             
22006         }
22007         
22008         
22009         return false;
22010     },
22011     
22012     hide : function()
22013     {
22014         this.el.setXY([0,0]);
22015         this.el.removeClass('in');
22016         this.el.hide();
22017         this.hoverState = null;
22018         this.maskEl.hide(); // always..
22019         this.fireEvent('hide', this);
22020     }
22021     
22022 });
22023
22024
22025 Roo.apply(Roo.bootstrap.Popover, {
22026
22027     alignment : {
22028         'left' : ['r-l', [-10,0], 'left bs-popover-left'],
22029         'right' : ['l-br', [10,0], 'right bs-popover-right'],
22030         'bottom' : ['t-b', [0,10], 'top bs-popover-top'],
22031         'top' : [ 'b-t', [0,-10], 'bottom bs-popover-bottom']
22032     },
22033     
22034     zIndex : 20001,
22035
22036     clickHander : false,
22037     
22038     
22039
22040     onMouseDown : function(e)
22041     {
22042         if (this.popups.length &&  !e.getTarget(".roo-popover")) {
22043             /// what is nothing is showing..
22044             this.hideAll();
22045         }
22046          
22047     },
22048     
22049     
22050     popups : [],
22051     
22052     register : function(popup)
22053     {
22054         if (!Roo.bootstrap.Popover.clickHandler) {
22055             Roo.bootstrap.Popover.clickHandler = Roo.get(document).on("mousedown", Roo.bootstrap.Popover.onMouseDown, Roo.bootstrap.Popover);
22056         }
22057         // hide other popups.
22058         popup.on('show', Roo.bootstrap.Popover.onShow,  popup);
22059         popup.on('hide', Roo.bootstrap.Popover.onHide,  popup);
22060         this.hideAll(); //<< why?
22061         //this.popups.push(popup);
22062     },
22063     hideAll : function()
22064     {
22065         this.popups.forEach(function(p) {
22066             p.hide();
22067         });
22068     },
22069     onShow : function() {
22070         Roo.bootstrap.Popover.popups.push(this);
22071     },
22072     onHide : function() {
22073         Roo.bootstrap.Popover.popups.remove(this);
22074     } 
22075
22076 });
22077 /**
22078  * @class Roo.bootstrap.PopoverNav
22079  * @extends Roo.bootstrap.nav.Simplebar
22080  * @parent Roo.bootstrap.Popover
22081  * @children Roo.bootstrap.nav.Group Roo.bootstrap.Container
22082  * @licence LGPL
22083  * Bootstrap Popover header navigation class
22084  * FIXME? should this go under nav?
22085  *
22086  * 
22087  * @constructor
22088  * Create a new Popover Header Navigation 
22089  * @param {Object} config The config object
22090  */
22091
22092 Roo.bootstrap.PopoverNav = function(config){
22093     Roo.bootstrap.PopoverNav.superclass.constructor.call(this, config);
22094 };
22095
22096 Roo.extend(Roo.bootstrap.PopoverNav, Roo.bootstrap.nav.Simplebar,  {
22097     
22098     
22099     container_method : 'getPopoverHeader' 
22100     
22101      
22102     
22103     
22104    
22105 });
22106
22107  
22108
22109  /*
22110  * - LGPL
22111  *
22112  * Progress
22113  * 
22114  */
22115
22116 /**
22117  * @class Roo.bootstrap.Progress
22118  * @extends Roo.bootstrap.Component
22119  * @children Roo.bootstrap.ProgressBar
22120  * Bootstrap Progress class
22121  * @cfg {Boolean} striped striped of the progress bar
22122  * @cfg {Boolean} active animated of the progress bar
22123  * 
22124  * 
22125  * @constructor
22126  * Create a new Progress
22127  * @param {Object} config The config object
22128  */
22129
22130 Roo.bootstrap.Progress = function(config){
22131     Roo.bootstrap.Progress.superclass.constructor.call(this, config);
22132 };
22133
22134 Roo.extend(Roo.bootstrap.Progress, Roo.bootstrap.Component,  {
22135     
22136     striped : false,
22137     active: false,
22138     
22139     getAutoCreate : function(){
22140         var cfg = {
22141             tag: 'div',
22142             cls: 'progress'
22143         };
22144         
22145         
22146         if(this.striped){
22147             cfg.cls += ' progress-striped';
22148         }
22149       
22150         if(this.active){
22151             cfg.cls += ' active';
22152         }
22153         
22154         
22155         return cfg;
22156     }
22157    
22158 });
22159
22160  
22161
22162  /*
22163  * - LGPL
22164  *
22165  * ProgressBar
22166  * 
22167  */
22168
22169 /**
22170  * @class Roo.bootstrap.ProgressBar
22171  * @extends Roo.bootstrap.Component
22172  * Bootstrap ProgressBar class
22173  * @cfg {Number} aria_valuenow aria-value now
22174  * @cfg {Number} aria_valuemin aria-value min
22175  * @cfg {Number} aria_valuemax aria-value max
22176  * @cfg {String} label label for the progress bar
22177  * @cfg {String} panel (success | info | warning | danger )
22178  * @cfg {String} role role of the progress bar
22179  * @cfg {String} sr_only text
22180  * 
22181  * 
22182  * @constructor
22183  * Create a new ProgressBar
22184  * @param {Object} config The config object
22185  */
22186
22187 Roo.bootstrap.ProgressBar = function(config){
22188     Roo.bootstrap.ProgressBar.superclass.constructor.call(this, config);
22189 };
22190
22191 Roo.extend(Roo.bootstrap.ProgressBar, Roo.bootstrap.Component,  {
22192     
22193     aria_valuenow : 0,
22194     aria_valuemin : 0,
22195     aria_valuemax : 100,
22196     label : false,
22197     panel : false,
22198     role : false,
22199     sr_only: false,
22200     
22201     getAutoCreate : function()
22202     {
22203         
22204         var cfg = {
22205             tag: 'div',
22206             cls: 'progress-bar',
22207             style: 'width:' + Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%'
22208         };
22209         
22210         if(this.sr_only){
22211             cfg.cn = {
22212                 tag: 'span',
22213                 cls: 'sr-only',
22214                 html: this.sr_only
22215             }
22216         }
22217         
22218         if(this.role){
22219             cfg.role = this.role;
22220         }
22221         
22222         if(this.aria_valuenow){
22223             cfg['aria-valuenow'] = this.aria_valuenow;
22224         }
22225         
22226         if(this.aria_valuemin){
22227             cfg['aria-valuemin'] = this.aria_valuemin;
22228         }
22229         
22230         if(this.aria_valuemax){
22231             cfg['aria-valuemax'] = this.aria_valuemax;
22232         }
22233         
22234         if(this.label && !this.sr_only){
22235             cfg.html = this.label;
22236         }
22237         
22238         if(this.panel){
22239             cfg.cls += ' progress-bar-' + this.panel;
22240         }
22241         
22242         return cfg;
22243     },
22244     
22245     update : function(aria_valuenow)
22246     {
22247         this.aria_valuenow = aria_valuenow;
22248         
22249         this.el.setStyle('width', Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%');
22250     }
22251    
22252 });
22253
22254  
22255
22256  /**
22257  * @class Roo.bootstrap.TabGroup
22258  * @extends Roo.bootstrap.Column
22259  * @children Roo.bootstrap.TabPanel
22260  * Bootstrap Column class
22261  * @cfg {String} navId the navigation id (for use with navbars) - will be auto generated if it does not exist..
22262  * @cfg {Boolean} carousel true to make the group behave like a carousel
22263  * @cfg {Boolean} bullets show bullets for the panels
22264  * @cfg {Boolean} autoslide (true|false) auto slide .. default false
22265  * @cfg {Number} timer auto slide timer .. default 0 millisecond
22266  * @cfg {Boolean} showarrow (true|false) show arrow default true
22267  * 
22268  * @constructor
22269  * Create a new TabGroup
22270  * @param {Object} config The config object
22271  */
22272
22273 Roo.bootstrap.TabGroup = function(config){
22274     Roo.bootstrap.TabGroup.superclass.constructor.call(this, config);
22275     if (!this.navId) {
22276         this.navId = Roo.id();
22277     }
22278     this.tabs = [];
22279     Roo.bootstrap.TabGroup.register(this);
22280     
22281 };
22282
22283 Roo.extend(Roo.bootstrap.TabGroup, Roo.bootstrap.Column,  {
22284     
22285     carousel : false,
22286     transition : false,
22287     bullets : 0,
22288     timer : 0,
22289     autoslide : false,
22290     slideFn : false,
22291     slideOnTouch : false,
22292     showarrow : true,
22293     
22294     getAutoCreate : function()
22295     {
22296         var cfg = Roo.apply({}, Roo.bootstrap.TabGroup.superclass.getAutoCreate.call(this));
22297         
22298         cfg.cls += ' tab-content';
22299         
22300         if (this.carousel) {
22301             cfg.cls += ' carousel slide';
22302             
22303             cfg.cn = [{
22304                cls : 'carousel-inner',
22305                cn : []
22306             }];
22307         
22308             if(this.bullets  && !Roo.isTouch){
22309                 
22310                 var bullets = {
22311                     cls : 'carousel-bullets',
22312                     cn : []
22313                 };
22314                
22315                 if(this.bullets_cls){
22316                     bullets.cls = bullets.cls + ' ' + this.bullets_cls;
22317                 }
22318                 
22319                 bullets.cn.push({
22320                     cls : 'clear'
22321                 });
22322                 
22323                 cfg.cn[0].cn.push(bullets);
22324             }
22325             
22326             if(this.showarrow){
22327                 cfg.cn[0].cn.push({
22328                     tag : 'div',
22329                     class : 'carousel-arrow',
22330                     cn : [
22331                         {
22332                             tag : 'div',
22333                             class : 'carousel-prev',
22334                             cn : [
22335                                 {
22336                                     tag : 'i',
22337                                     class : 'fa fa-chevron-left'
22338                                 }
22339                             ]
22340                         },
22341                         {
22342                             tag : 'div',
22343                             class : 'carousel-next',
22344                             cn : [
22345                                 {
22346                                     tag : 'i',
22347                                     class : 'fa fa-chevron-right'
22348                                 }
22349                             ]
22350                         }
22351                     ]
22352                 });
22353             }
22354             
22355         }
22356         
22357         return cfg;
22358     },
22359     
22360     initEvents:  function()
22361     {
22362 //        if(Roo.isTouch && this.slideOnTouch && !this.showarrow){
22363 //            this.el.on("touchstart", this.onTouchStart, this);
22364 //        }
22365         
22366         if(this.autoslide){
22367             var _this = this;
22368             
22369             this.slideFn = window.setInterval(function() {
22370                 _this.showPanelNext();
22371             }, this.timer);
22372         }
22373         
22374         if(this.showarrow){
22375             this.el.select('.carousel-prev', true).first().on('click', this.showPanelPrev, this);
22376             this.el.select('.carousel-next', true).first().on('click', this.showPanelNext, this);
22377         }
22378         
22379         
22380     },
22381     
22382 //    onTouchStart : function(e, el, o)
22383 //    {
22384 //        if(!this.slideOnTouch || !Roo.isTouch || Roo.get(e.getTarget()).hasClass('roo-button-text')){
22385 //            return;
22386 //        }
22387 //        
22388 //        this.showPanelNext();
22389 //    },
22390     
22391     
22392     getChildContainer : function()
22393     {
22394         return this.carousel ? this.el.select('.carousel-inner', true).first() : this.el;
22395     },
22396     
22397     /**
22398     * register a Navigation item
22399     * @param {Roo.bootstrap.nav.Item} the navitem to add
22400     */
22401     register : function(item)
22402     {
22403         this.tabs.push( item);
22404         item.navId = this.navId; // not really needed..
22405         this.addBullet();
22406     
22407     },
22408     
22409     getActivePanel : function()
22410     {
22411         var r = false;
22412         Roo.each(this.tabs, function(t) {
22413             if (t.active) {
22414                 r = t;
22415                 return false;
22416             }
22417             return null;
22418         });
22419         return r;
22420         
22421     },
22422     getPanelByName : function(n)
22423     {
22424         var r = false;
22425         Roo.each(this.tabs, function(t) {
22426             if (t.tabId == n) {
22427                 r = t;
22428                 return false;
22429             }
22430             return null;
22431         });
22432         return r;
22433     },
22434     indexOfPanel : function(p)
22435     {
22436         var r = false;
22437         Roo.each(this.tabs, function(t,i) {
22438             if (t.tabId == p.tabId) {
22439                 r = i;
22440                 return false;
22441             }
22442             return null;
22443         });
22444         return r;
22445     },
22446     /**
22447      * show a specific panel
22448      * @param {Roo.bootstrap.TabPanel|number|string} panel to change to (use the tabId to specify a specific one)
22449      * @return {boolean} false if panel was not shown (invalid entry or beforedeactivate fails.)
22450      */
22451     showPanel : function (pan)
22452     {
22453         if(this.transition || typeof(pan) == 'undefined'){
22454             Roo.log("waiting for the transitionend");
22455             return false;
22456         }
22457         
22458         if (typeof(pan) == 'number') {
22459             pan = this.tabs[pan];
22460         }
22461         
22462         if (typeof(pan) == 'string') {
22463             pan = this.getPanelByName(pan);
22464         }
22465         
22466         var cur = this.getActivePanel();
22467         
22468         if(!pan || !cur){
22469             Roo.log('pan or acitve pan is undefined');
22470             return false;
22471         }
22472         
22473         if (pan.tabId == this.getActivePanel().tabId) {
22474             return true;
22475         }
22476         
22477         if (false === cur.fireEvent('beforedeactivate')) {
22478             return false;
22479         }
22480         
22481         if(this.bullets > 0 && !Roo.isTouch){
22482             this.setActiveBullet(this.indexOfPanel(pan));
22483         }
22484         
22485         if (this.carousel && typeof(Roo.get(document.body).dom.style.transition) != 'undefined') {
22486             
22487             //class="carousel-item carousel-item-next carousel-item-left"
22488             
22489             this.transition = true;
22490             var dir = this.indexOfPanel(pan) > this.indexOfPanel(cur)  ? 'next' : 'prev';
22491             var lr = dir == 'next' ? 'left' : 'right';
22492             pan.el.addClass(dir); // or prev
22493             pan.el.addClass('carousel-item-' + dir); // or prev
22494             pan.el.dom.offsetWidth; // find the offset with - causing a reflow?
22495             cur.el.addClass(lr); // or right
22496             pan.el.addClass(lr);
22497             cur.el.addClass('carousel-item-' +lr); // or right
22498             pan.el.addClass('carousel-item-' +lr);
22499             
22500             
22501             var _this = this;
22502             cur.el.on('transitionend', function() {
22503                 Roo.log("trans end?");
22504                 
22505                 pan.el.removeClass([lr,dir, 'carousel-item-' + lr, 'carousel-item-' + dir]);
22506                 pan.setActive(true);
22507                 
22508                 cur.el.removeClass([lr, 'carousel-item-' + lr]);
22509                 cur.setActive(false);
22510                 
22511                 _this.transition = false;
22512                 
22513             }, this, { single:  true } );
22514             
22515             return true;
22516         }
22517         
22518         cur.setActive(false);
22519         pan.setActive(true);
22520         
22521         return true;
22522         
22523     },
22524     showPanelNext : function()
22525     {
22526         var i = this.indexOfPanel(this.getActivePanel());
22527         
22528         if (i >= this.tabs.length - 1 && !this.autoslide) {
22529             return;
22530         }
22531         
22532         if (i >= this.tabs.length - 1 && this.autoslide) {
22533             i = -1;
22534         }
22535         
22536         this.showPanel(this.tabs[i+1]);
22537     },
22538     
22539     showPanelPrev : function()
22540     {
22541         var i = this.indexOfPanel(this.getActivePanel());
22542         
22543         if (i  < 1 && !this.autoslide) {
22544             return;
22545         }
22546         
22547         if (i < 1 && this.autoslide) {
22548             i = this.tabs.length;
22549         }
22550         
22551         this.showPanel(this.tabs[i-1]);
22552     },
22553     
22554     
22555     addBullet: function()
22556     {
22557         if(!this.bullets || Roo.isTouch){
22558             return;
22559         }
22560         var ctr = this.el.select('.carousel-bullets',true).first();
22561         var i = this.el.select('.carousel-bullets .bullet',true).getCount() ;
22562         var bullet = ctr.createChild({
22563             cls : 'bullet bullet-' + i
22564         },ctr.dom.lastChild);
22565         
22566         
22567         var _this = this;
22568         
22569         bullet.on('click', (function(e, el, o, ii, t){
22570
22571             e.preventDefault();
22572
22573             this.showPanel(ii);
22574
22575             if(this.autoslide && this.slideFn){
22576                 clearInterval(this.slideFn);
22577                 this.slideFn = window.setInterval(function() {
22578                     _this.showPanelNext();
22579                 }, this.timer);
22580             }
22581
22582         }).createDelegate(this, [i, bullet], true));
22583                 
22584         
22585     },
22586      
22587     setActiveBullet : function(i)
22588     {
22589         if(Roo.isTouch){
22590             return;
22591         }
22592         
22593         Roo.each(this.el.select('.bullet', true).elements, function(el){
22594             el.removeClass('selected');
22595         });
22596
22597         var bullet = this.el.select('.bullet-' + i, true).first();
22598         
22599         if(!bullet){
22600             return;
22601         }
22602         
22603         bullet.addClass('selected');
22604     }
22605     
22606     
22607   
22608 });
22609
22610  
22611
22612  
22613  
22614 Roo.apply(Roo.bootstrap.TabGroup, {
22615     
22616     groups: {},
22617      /**
22618     * register a Navigation Group
22619     * @param {Roo.bootstrap.nav.Group} the navgroup to add
22620     */
22621     register : function(navgrp)
22622     {
22623         this.groups[navgrp.navId] = navgrp;
22624         
22625     },
22626     /**
22627     * fetch a Navigation Group based on the navigation ID
22628     * if one does not exist , it will get created.
22629     * @param {string} the navgroup to add
22630     * @returns {Roo.bootstrap.nav.Group} the navgroup 
22631     */
22632     get: function(navId) {
22633         if (typeof(this.groups[navId]) == 'undefined') {
22634             this.register(new Roo.bootstrap.TabGroup({ navId : navId }));
22635         }
22636         return this.groups[navId] ;
22637     }
22638     
22639     
22640     
22641 });
22642
22643  /*
22644  * - LGPL
22645  *
22646  * TabPanel
22647  * 
22648  */
22649
22650 /**
22651  * @class Roo.bootstrap.TabPanel
22652  * @extends Roo.bootstrap.Component
22653  * @children Roo.bootstrap.Component
22654  * Bootstrap TabPanel class
22655  * @cfg {Boolean} active panel active
22656  * @cfg {String} html panel content
22657  * @cfg {String} tabId  unique tab ID (will be autogenerated if not set. - used to match TabItem to Panel)
22658  * @cfg {String} navId The Roo.bootstrap.nav.Group which triggers show hide ()
22659  * @cfg {String} href click to link..
22660  * @cfg {Boolean} touchSlide if swiping slides tab to next panel (default off)
22661  * 
22662  * 
22663  * @constructor
22664  * Create a new TabPanel
22665  * @param {Object} config The config object
22666  */
22667
22668 Roo.bootstrap.TabPanel = function(config){
22669     Roo.bootstrap.TabPanel.superclass.constructor.call(this, config);
22670     this.addEvents({
22671         /**
22672              * @event changed
22673              * Fires when the active status changes
22674              * @param {Roo.bootstrap.TabPanel} this
22675              * @param {Boolean} state the new state
22676             
22677          */
22678         'changed': true,
22679         /**
22680              * @event beforedeactivate
22681              * Fires before a tab is de-activated - can be used to do validation on a form.
22682              * @param {Roo.bootstrap.TabPanel} this
22683              * @return {Boolean} false if there is an error
22684             
22685          */
22686         'beforedeactivate': true
22687      });
22688     
22689     this.tabId = this.tabId || Roo.id();
22690   
22691 };
22692
22693 Roo.extend(Roo.bootstrap.TabPanel, Roo.bootstrap.Component,  {
22694     
22695     active: false,
22696     html: false,
22697     tabId: false,
22698     navId : false,
22699     href : '',
22700     touchSlide : false,
22701     getAutoCreate : function(){
22702         
22703         
22704         var cfg = {
22705             tag: 'div',
22706             // item is needed for carousel - not sure if it has any effect otherwise
22707             cls: 'carousel-item tab-pane item' + ((this.href.length) ? ' clickable ' : ''),
22708             html: this.html || ''
22709         };
22710         
22711         if(this.active){
22712             cfg.cls += ' active';
22713         }
22714         
22715         if(this.tabId){
22716             cfg.tabId = this.tabId;
22717         }
22718         
22719         
22720         
22721         return cfg;
22722     },
22723     
22724     initEvents:  function()
22725     {
22726         var p = this.parent();
22727         
22728         this.navId = this.navId || p.navId;
22729         
22730         if (typeof(this.navId) != 'undefined') {
22731             // not really needed.. but just in case.. parent should be a NavGroup.
22732             var tg = Roo.bootstrap.TabGroup.get(this.navId);
22733             
22734             tg.register(this);
22735             
22736             var i = tg.tabs.length - 1;
22737             
22738             if(this.active && tg.bullets > 0 && i < tg.bullets){
22739                 tg.setActiveBullet(i);
22740             }
22741         }
22742         
22743         this.el.on('click', this.onClick, this);
22744         
22745         if(Roo.isTouch && this.touchSlide){
22746             this.el.on("touchstart", this.onTouchStart, this);
22747             this.el.on("touchmove", this.onTouchMove, this);
22748             this.el.on("touchend", this.onTouchEnd, this);
22749         }
22750         
22751     },
22752     
22753     onRender : function(ct, position)
22754     {
22755         Roo.bootstrap.TabPanel.superclass.onRender.call(this, ct, position);
22756     },
22757     
22758     setActive : function(state)
22759     {
22760         Roo.log("panel - set active " + this.tabId + "=" + state);
22761         
22762         this.active = state;
22763         if (!state) {
22764             this.el.removeClass('active');
22765             
22766         } else  if (!this.el.hasClass('active')) {
22767             this.el.addClass('active');
22768         }
22769         
22770         this.fireEvent('changed', this, state);
22771     },
22772     
22773     onClick : function(e)
22774     {
22775         e.preventDefault();
22776         
22777         if(!this.href.length){
22778             return;
22779         }
22780         
22781         window.location.href = this.href;
22782     },
22783     
22784     startX : 0,
22785     startY : 0,
22786     endX : 0,
22787     endY : 0,
22788     swiping : false,
22789     
22790     onTouchStart : function(e)
22791     {
22792         this.swiping = false;
22793         
22794         this.startX = e.browserEvent.touches[0].clientX;
22795         this.startY = e.browserEvent.touches[0].clientY;
22796     },
22797     
22798     onTouchMove : function(e)
22799     {
22800         this.swiping = true;
22801         
22802         this.endX = e.browserEvent.touches[0].clientX;
22803         this.endY = e.browserEvent.touches[0].clientY;
22804     },
22805     
22806     onTouchEnd : function(e)
22807     {
22808         if(!this.swiping){
22809             this.onClick(e);
22810             return;
22811         }
22812         
22813         var tabGroup = this.parent();
22814         
22815         if(this.endX > this.startX){ // swiping right
22816             tabGroup.showPanelPrev();
22817             return;
22818         }
22819         
22820         if(this.startX > this.endX){ // swiping left
22821             tabGroup.showPanelNext();
22822             return;
22823         }
22824     }
22825     
22826     
22827 });
22828  
22829
22830  
22831
22832  /*
22833  * - LGPL
22834  *
22835  * DateField
22836  * 
22837  */
22838
22839 /**
22840  * @class Roo.bootstrap.form.DateField
22841  * @extends Roo.bootstrap.form.Input
22842  * Bootstrap DateField class
22843  * @cfg {Number} weekStart default 0
22844  * @cfg {String} viewMode default empty, (months|years)
22845  * @cfg {String} minViewMode default empty, (months|years)
22846  * @cfg {Number} startDate default -Infinity
22847  * @cfg {Number} endDate default Infinity
22848  * @cfg {Boolean} todayHighlight default false
22849  * @cfg {Boolean} todayBtn default false
22850  * @cfg {Boolean} calendarWeeks default false
22851  * @cfg {Object} daysOfWeekDisabled default empty
22852  * @cfg {Boolean} singleMode default false (true | false)
22853  * 
22854  * @cfg {Boolean} keyboardNavigation default true
22855  * @cfg {String} language default en
22856  * 
22857  * @constructor
22858  * Create a new DateField
22859  * @param {Object} config The config object
22860  */
22861
22862 Roo.bootstrap.form.DateField = function(config){
22863     Roo.bootstrap.form.DateField.superclass.constructor.call(this, config);
22864      this.addEvents({
22865             /**
22866              * @event show
22867              * Fires when this field show.
22868              * @param {Roo.bootstrap.form.DateField} this
22869              * @param {Mixed} date The date value
22870              */
22871             show : true,
22872             /**
22873              * @event show
22874              * Fires when this field hide.
22875              * @param {Roo.bootstrap.form.DateField} this
22876              * @param {Mixed} date The date value
22877              */
22878             hide : true,
22879             /**
22880              * @event select
22881              * Fires when select a date.
22882              * @param {Roo.bootstrap.form.DateField} this
22883              * @param {Mixed} date The date value
22884              */
22885             select : true,
22886             /**
22887              * @event beforeselect
22888              * Fires when before select a date.
22889              * @param {Roo.bootstrap.form.DateField} this
22890              * @param {Mixed} date The date value
22891              */
22892             beforeselect : true
22893         });
22894 };
22895
22896 Roo.extend(Roo.bootstrap.form.DateField, Roo.bootstrap.form.Input,  {
22897     
22898     /**
22899      * @cfg {String} format
22900      * The default date format string which can be overriden for localization support.  The format must be
22901      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
22902      */
22903     format : "m/d/y",
22904     /**
22905      * @cfg {String} altFormats
22906      * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
22907      * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
22908      */
22909     altFormats : "m/d/Y|m-d-y|m-d-Y|m/d|m-d|md|mdy|mdY|d",
22910     
22911     weekStart : 0,
22912     
22913     viewMode : '',
22914     
22915     minViewMode : '',
22916     
22917     todayHighlight : false,
22918     
22919     todayBtn: false,
22920     
22921     language: 'en',
22922     
22923     keyboardNavigation: true,
22924     
22925     calendarWeeks: false,
22926     
22927     startDate: -Infinity,
22928     
22929     endDate: Infinity,
22930     
22931     daysOfWeekDisabled: [],
22932     
22933     _events: [],
22934     
22935     singleMode : false,
22936     
22937     UTCDate: function()
22938     {
22939         return new Date(Date.UTC.apply(Date, arguments));
22940     },
22941     
22942     UTCToday: function()
22943     {
22944         var today = new Date();
22945         return this.UTCDate(today.getUTCFullYear(), today.getUTCMonth(), today.getUTCDate());
22946     },
22947     
22948     getDate: function() {
22949             var d = this.getUTCDate();
22950             return new Date(d.getTime() + (d.getTimezoneOffset()*60000));
22951     },
22952     
22953     getUTCDate: function() {
22954             return this.date;
22955     },
22956     
22957     setDate: function(d) {
22958             this.setUTCDate(new Date(d.getTime() - (d.getTimezoneOffset()*60000)));
22959     },
22960     
22961     setUTCDate: function(d) {
22962             this.date = d;
22963             this.setValue(this.formatDate(this.date));
22964     },
22965         
22966     onRender: function(ct, position)
22967     {
22968         
22969         Roo.bootstrap.form.DateField.superclass.onRender.call(this, ct, position);
22970         
22971         this.language = this.language || 'en';
22972         this.language = this.language in Roo.bootstrap.form.DateField.dates ? this.language : this.language.split('-')[0];
22973         this.language = this.language in Roo.bootstrap.form.DateField.dates ? this.language : "en";
22974         
22975         this.isRTL = Roo.bootstrap.form.DateField.dates[this.language].rtl || false;
22976         this.format = this.format || 'm/d/y';
22977         this.isInline = false;
22978         this.isInput = true;
22979         this.component = this.el.select('.add-on', true).first() || false;
22980         this.component = (this.component && this.component.length === 0) ? false : this.component;
22981         this.hasInput = this.component && this.inputEl().length;
22982         
22983         if (typeof(this.minViewMode === 'string')) {
22984             switch (this.minViewMode) {
22985                 case 'months':
22986                     this.minViewMode = 1;
22987                     break;
22988                 case 'years':
22989                     this.minViewMode = 2;
22990                     break;
22991                 default:
22992                     this.minViewMode = 0;
22993                     break;
22994             }
22995         }
22996         
22997         if (typeof(this.viewMode === 'string')) {
22998             switch (this.viewMode) {
22999                 case 'months':
23000                     this.viewMode = 1;
23001                     break;
23002                 case 'years':
23003                     this.viewMode = 2;
23004                     break;
23005                 default:
23006                     this.viewMode = 0;
23007                     break;
23008             }
23009         }
23010                 
23011         this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.form.DateField.template);
23012         
23013 //        this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.form.DateField.template);
23014         
23015         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
23016         
23017         this.picker().on('mousedown', this.onMousedown, this);
23018         this.picker().on('click', this.onClick, this);
23019         
23020         this.picker().addClass('datepicker-dropdown');
23021         
23022         this.startViewMode = this.viewMode;
23023         
23024         if(this.singleMode){
23025             Roo.each(this.picker().select('thead > tr > th', true).elements, function(v){
23026                 v.setVisibilityMode(Roo.Element.DISPLAY);
23027                 v.hide();
23028             });
23029             
23030             Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
23031                 v.setStyle('width', '189px');
23032             });
23033         }
23034         
23035         Roo.each(this.picker().select('tfoot th.today', true).elements, function(v){
23036             if(!this.calendarWeeks){
23037                 v.remove();
23038                 return;
23039             }
23040             
23041             v.dom.innerHTML = Roo.bootstrap.form.DateField.dates[this.language].today;
23042             v.attr('colspan', function(i, val){
23043                 return parseInt(val) + 1;
23044             });
23045         });
23046                         
23047         
23048         this.weekEnd = this.weekStart === 0 ? 6 : this.weekStart - 1;
23049         
23050         this.setStartDate(this.startDate);
23051         this.setEndDate(this.endDate);
23052         
23053         this.setDaysOfWeekDisabled(this.daysOfWeekDisabled);
23054         
23055         this.fillDow();
23056         this.fillMonths();
23057         this.update();
23058         this.showMode();
23059         
23060         if(this.isInline) {
23061             this.showPopup();
23062         }
23063     },
23064     
23065     picker : function()
23066     {
23067         return this.pickerEl;
23068 //        return this.el.select('.datepicker', true).first();
23069     },
23070     
23071     fillDow: function()
23072     {
23073         var dowCnt = this.weekStart;
23074         
23075         var dow = {
23076             tag: 'tr',
23077             cn: [
23078                 
23079             ]
23080         };
23081         
23082         if(this.calendarWeeks){
23083             dow.cn.push({
23084                 tag: 'th',
23085                 cls: 'cw',
23086                 html: '&nbsp;'
23087             })
23088         }
23089         
23090         while (dowCnt < this.weekStart + 7) {
23091             dow.cn.push({
23092                 tag: 'th',
23093                 cls: 'dow',
23094                 html: Roo.bootstrap.form.DateField.dates[this.language].daysMin[(dowCnt++)%7]
23095             });
23096         }
23097         
23098         this.picker().select('>.datepicker-days thead', true).first().createChild(dow);
23099     },
23100     
23101     fillMonths: function()
23102     {    
23103         var i = 0;
23104         var months = this.picker().select('>.datepicker-months td', true).first();
23105         
23106         months.dom.innerHTML = '';
23107         
23108         while (i < 12) {
23109             var month = {
23110                 tag: 'span',
23111                 cls: 'month',
23112                 html: Roo.bootstrap.form.DateField.dates[this.language].monthsShort[i++]
23113             };
23114             
23115             months.createChild(month);
23116         }
23117         
23118     },
23119     
23120     update: function()
23121     {
23122         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;
23123         
23124         if (this.date < this.startDate) {
23125             this.viewDate = new Date(this.startDate);
23126         } else if (this.date > this.endDate) {
23127             this.viewDate = new Date(this.endDate);
23128         } else {
23129             this.viewDate = new Date(this.date);
23130         }
23131         
23132         this.fill();
23133     },
23134     
23135     fill: function() 
23136     {
23137         var d = new Date(this.viewDate),
23138                 year = d.getUTCFullYear(),
23139                 month = d.getUTCMonth(),
23140                 startYear = this.startDate !== -Infinity ? this.startDate.getUTCFullYear() : -Infinity,
23141                 startMonth = this.startDate !== -Infinity ? this.startDate.getUTCMonth() : -Infinity,
23142                 endYear = this.endDate !== Infinity ? this.endDate.getUTCFullYear() : Infinity,
23143                 endMonth = this.endDate !== Infinity ? this.endDate.getUTCMonth() : Infinity,
23144                 currentDate = this.date && this.date.valueOf(),
23145                 today = this.UTCToday();
23146         
23147         this.picker().select('>.datepicker-days thead th.switch', true).first().dom.innerHTML = Roo.bootstrap.form.DateField.dates[this.language].months[month]+' '+year;
23148         
23149 //        this.picker().select('>tfoot th.today', true).first().dom.innerHTML = Roo.bootstrap.form.DateField.dates[this.language].today;
23150         
23151 //        this.picker.select('>tfoot th.today').
23152 //                                              .text(dates[this.language].today)
23153 //                                              .toggle(this.todayBtn !== false);
23154     
23155         this.updateNavArrows();
23156         this.fillMonths();
23157                                                 
23158         var prevMonth = this.UTCDate(year, month-1, 28,0,0,0,0),
23159         
23160         day = prevMonth.getDaysInMonth(prevMonth.getUTCFullYear(), prevMonth.getUTCMonth());
23161          
23162         prevMonth.setUTCDate(day);
23163         
23164         prevMonth.setUTCDate(day - (prevMonth.getUTCDay() - this.weekStart + 7)%7);
23165         
23166         var nextMonth = new Date(prevMonth);
23167         
23168         nextMonth.setUTCDate(nextMonth.getUTCDate() + 42);
23169         
23170         nextMonth = nextMonth.valueOf();
23171         
23172         var fillMonths = false;
23173         
23174         this.picker().select('>.datepicker-days tbody',true).first().dom.innerHTML = '';
23175         
23176         while(prevMonth.valueOf() <= nextMonth) {
23177             var clsName = '';
23178             
23179             if (prevMonth.getUTCDay() === this.weekStart) {
23180                 if(fillMonths){
23181                     this.picker().select('>.datepicker-days tbody',true).first().createChild(fillMonths);
23182                 }
23183                     
23184                 fillMonths = {
23185                     tag: 'tr',
23186                     cn: []
23187                 };
23188                 
23189                 if(this.calendarWeeks){
23190                     // ISO 8601: First week contains first thursday.
23191                     // ISO also states week starts on Monday, but we can be more abstract here.
23192                     var
23193                     // Start of current week: based on weekstart/current date
23194                     ws = new Date(+prevMonth + (this.weekStart - prevMonth.getUTCDay() - 7) % 7 * 864e5),
23195                     // Thursday of this week
23196                     th = new Date(+ws + (7 + 4 - ws.getUTCDay()) % 7 * 864e5),
23197                     // First Thursday of year, year from thursday
23198                     yth = new Date(+(yth = this.UTCDate(th.getUTCFullYear(), 0, 1)) + (7 + 4 - yth.getUTCDay())%7*864e5),
23199                     // Calendar week: ms between thursdays, div ms per day, div 7 days
23200                     calWeek =  (th - yth) / 864e5 / 7 + 1;
23201                     
23202                     fillMonths.cn.push({
23203                         tag: 'td',
23204                         cls: 'cw',
23205                         html: calWeek
23206                     });
23207                 }
23208             }
23209             
23210             if (prevMonth.getUTCFullYear() < year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() < month)) {
23211                 clsName += ' old';
23212             } else if (prevMonth.getUTCFullYear() > year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() > month)) {
23213                 clsName += ' new';
23214             }
23215             if (this.todayHighlight &&
23216                 prevMonth.getUTCFullYear() == today.getFullYear() &&
23217                 prevMonth.getUTCMonth() == today.getMonth() &&
23218                 prevMonth.getUTCDate() == today.getDate()) {
23219                 clsName += ' today';
23220             }
23221             
23222             if (currentDate && prevMonth.valueOf() === currentDate) {
23223                 clsName += ' active';
23224             }
23225             
23226             if (prevMonth.valueOf() < this.startDate || prevMonth.valueOf() > this.endDate ||
23227                     this.daysOfWeekDisabled.indexOf(prevMonth.getUTCDay()) !== -1) {
23228                     clsName += ' disabled';
23229             }
23230             
23231             fillMonths.cn.push({
23232                 tag: 'td',
23233                 cls: 'day ' + clsName,
23234                 html: prevMonth.getDate()
23235             });
23236             
23237             prevMonth.setDate(prevMonth.getDate()+1);
23238         }
23239           
23240         var currentYear = this.date && this.date.getUTCFullYear();
23241         var currentMonth = this.date && this.date.getUTCMonth();
23242         
23243         this.picker().select('>.datepicker-months th.switch',true).first().dom.innerHTML = year;
23244         
23245         Roo.each(this.picker().select('>.datepicker-months tbody span',true).elements, function(v,k){
23246             v.removeClass('active');
23247             
23248             if(currentYear === year && k === currentMonth){
23249                 v.addClass('active');
23250             }
23251             
23252             if (year < startYear || year > endYear || (year == startYear && k < startMonth) || (year == endYear && k > endMonth)) {
23253                 v.addClass('disabled');
23254             }
23255             
23256         });
23257         
23258         
23259         year = parseInt(year/10, 10) * 10;
23260         
23261         this.picker().select('>.datepicker-years th.switch', true).first().dom.innerHTML = year + '-' + (year + 9);
23262         
23263         this.picker().select('>.datepicker-years tbody td',true).first().dom.innerHTML = '';
23264         
23265         year -= 1;
23266         for (var i = -1; i < 11; i++) {
23267             this.picker().select('>.datepicker-years tbody td',true).first().createChild({
23268                 tag: 'span',
23269                 cls: 'year' + (i === -1 || i === 10 ? ' old' : '') + (currentYear === year ? ' active' : '') + (year < startYear || year > endYear ? ' disabled' : ''),
23270                 html: year
23271             });
23272             
23273             year += 1;
23274         }
23275     },
23276     
23277     showMode: function(dir) 
23278     {
23279         if (dir) {
23280             this.viewMode = Math.max(this.minViewMode, Math.min(2, this.viewMode + dir));
23281         }
23282         
23283         Roo.each(this.picker().select('>div',true).elements, function(v){
23284             v.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
23285             v.hide();
23286         });
23287         this.picker().select('>.datepicker-'+Roo.bootstrap.form.DateField.modes[this.viewMode].clsName, true).first().show();
23288     },
23289     
23290     place: function()
23291     {
23292         if(this.isInline) {
23293             return;
23294         }
23295         
23296         this.picker().removeClass(['bottom', 'top']);
23297         
23298         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
23299             /*
23300              * place to the top of element!
23301              *
23302              */
23303             
23304             this.picker().addClass('top');
23305             this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
23306             
23307             return;
23308         }
23309         
23310         this.picker().addClass('bottom');
23311         
23312         this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
23313     },
23314     
23315     parseDate : function(value)
23316     {
23317         if(!value || value instanceof Date){
23318             return value;
23319         }
23320         var v = Date.parseDate(value, this.format);
23321         if (!v && (this.useIso || value.match(/^(\d{4})-0?(\d+)-0?(\d+)/))) {
23322             v = Date.parseDate(value, 'Y-m-d');
23323         }
23324         if(!v && this.altFormats){
23325             if(!this.altFormatsArray){
23326                 this.altFormatsArray = this.altFormats.split("|");
23327             }
23328             for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
23329                 v = Date.parseDate(value, this.altFormatsArray[i]);
23330             }
23331         }
23332         return v;
23333     },
23334     
23335     formatDate : function(date, fmt)
23336     {   
23337         return (!date || !(date instanceof Date)) ?
23338         date : date.dateFormat(fmt || this.format);
23339     },
23340     
23341     onFocus : function()
23342     {
23343         Roo.bootstrap.form.DateField.superclass.onFocus.call(this);
23344         this.showPopup();
23345     },
23346     
23347     onBlur : function()
23348     {
23349         Roo.bootstrap.form.DateField.superclass.onBlur.call(this);
23350         
23351         var d = this.inputEl().getValue();
23352         
23353         this.setValue(d);
23354                 
23355         this.hidePopup();
23356     },
23357     
23358     showPopup : function()
23359     {
23360         this.picker().show();
23361         this.update();
23362         this.place();
23363         
23364         this.fireEvent('showpopup', this, this.date);
23365     },
23366     
23367     hidePopup : function()
23368     {
23369         if(this.isInline) {
23370             return;
23371         }
23372         this.picker().hide();
23373         this.viewMode = this.startViewMode;
23374         this.showMode();
23375         
23376         this.fireEvent('hidepopup', this, this.date);
23377         
23378     },
23379     
23380     onMousedown: function(e)
23381     {
23382         e.stopPropagation();
23383         e.preventDefault();
23384     },
23385     
23386     keyup: function(e)
23387     {
23388         Roo.bootstrap.form.DateField.superclass.keyup.call(this);
23389         this.update();
23390     },
23391
23392     setValue: function(v)
23393     {
23394         if(this.fireEvent('beforeselect', this, v) !== false){
23395             var d = new Date(this.parseDate(v) ).clearTime();
23396         
23397             if(isNaN(d.getTime())){
23398                 this.date = this.viewDate = '';
23399                 Roo.bootstrap.form.DateField.superclass.setValue.call(this, '');
23400                 return;
23401             }
23402
23403             v = this.formatDate(d);
23404
23405             Roo.bootstrap.form.DateField.superclass.setValue.call(this, v);
23406
23407             this.date = new Date(d.getTime() - d.getTimezoneOffset()*60000);
23408
23409             this.update();
23410
23411             this.fireEvent('select', this, this.date);
23412         }
23413     },
23414     
23415     getValue: function()
23416     {
23417         return this.formatDate(this.date);
23418     },
23419     
23420     fireKey: function(e)
23421     {
23422         if (!this.picker().isVisible()){
23423             if (e.keyCode == 27) { // allow escape to hide and re-show picker
23424                 this.showPopup();
23425             }
23426             return;
23427         }
23428         
23429         var dateChanged = false,
23430         dir, day, month,
23431         newDate, newViewDate;
23432         
23433         switch(e.keyCode){
23434             case 27: // escape
23435                 this.hidePopup();
23436                 e.preventDefault();
23437                 break;
23438             case 37: // left
23439             case 39: // right
23440                 if (!this.keyboardNavigation) {
23441                     break;
23442                 }
23443                 dir = e.keyCode == 37 ? -1 : 1;
23444                 
23445                 if (e.ctrlKey){
23446                     newDate = this.moveYear(this.date, dir);
23447                     newViewDate = this.moveYear(this.viewDate, dir);
23448                 } else if (e.shiftKey){
23449                     newDate = this.moveMonth(this.date, dir);
23450                     newViewDate = this.moveMonth(this.viewDate, dir);
23451                 } else {
23452                     newDate = new Date(this.date);
23453                     newDate.setUTCDate(this.date.getUTCDate() + dir);
23454                     newViewDate = new Date(this.viewDate);
23455                     newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir);
23456                 }
23457                 if (this.dateWithinRange(newDate)){
23458                     this.date = newDate;
23459                     this.viewDate = newViewDate;
23460                     this.setValue(this.formatDate(this.date));
23461 //                    this.update();
23462                     e.preventDefault();
23463                     dateChanged = true;
23464                 }
23465                 break;
23466             case 38: // up
23467             case 40: // down
23468                 if (!this.keyboardNavigation) {
23469                     break;
23470                 }
23471                 dir = e.keyCode == 38 ? -1 : 1;
23472                 if (e.ctrlKey){
23473                     newDate = this.moveYear(this.date, dir);
23474                     newViewDate = this.moveYear(this.viewDate, dir);
23475                 } else if (e.shiftKey){
23476                     newDate = this.moveMonth(this.date, dir);
23477                     newViewDate = this.moveMonth(this.viewDate, dir);
23478                 } else {
23479                     newDate = new Date(this.date);
23480                     newDate.setUTCDate(this.date.getUTCDate() + dir * 7);
23481                     newViewDate = new Date(this.viewDate);
23482                     newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir * 7);
23483                 }
23484                 if (this.dateWithinRange(newDate)){
23485                     this.date = newDate;
23486                     this.viewDate = newViewDate;
23487                     this.setValue(this.formatDate(this.date));
23488 //                    this.update();
23489                     e.preventDefault();
23490                     dateChanged = true;
23491                 }
23492                 break;
23493             case 13: // enter
23494                 this.setValue(this.formatDate(this.date));
23495                 this.hidePopup();
23496                 e.preventDefault();
23497                 break;
23498             case 9: // tab
23499                 this.setValue(this.formatDate(this.date));
23500                 this.hidePopup();
23501                 break;
23502             case 16: // shift
23503             case 17: // ctrl
23504             case 18: // alt
23505                 break;
23506             default :
23507                 this.hidePopup();
23508                 
23509         }
23510     },
23511     
23512     
23513     onClick: function(e) 
23514     {
23515         e.stopPropagation();
23516         e.preventDefault();
23517         
23518         var target = e.getTarget();
23519         
23520         if(target.nodeName.toLowerCase() === 'i'){
23521             target = Roo.get(target).dom.parentNode;
23522         }
23523         
23524         var nodeName = target.nodeName;
23525         var className = target.className;
23526         var html = target.innerHTML;
23527         //Roo.log(nodeName);
23528         
23529         switch(nodeName.toLowerCase()) {
23530             case 'th':
23531                 switch(className) {
23532                     case 'switch':
23533                         this.showMode(1);
23534                         break;
23535                     case 'prev':
23536                     case 'next':
23537                         var dir = Roo.bootstrap.form.DateField.modes[this.viewMode].navStep * (className == 'prev' ? -1 : 1);
23538                         switch(this.viewMode){
23539                                 case 0:
23540                                         this.viewDate = this.moveMonth(this.viewDate, dir);
23541                                         break;
23542                                 case 1:
23543                                 case 2:
23544                                         this.viewDate = this.moveYear(this.viewDate, dir);
23545                                         break;
23546                         }
23547                         this.fill();
23548                         break;
23549                     case 'today':
23550                         var date = new Date();
23551                         this.date = this.UTCDate(date.getFullYear(), date.getMonth(), date.getDate(), 0, 0, 0);
23552 //                        this.fill()
23553                         this.setValue(this.formatDate(this.date));
23554                         
23555                         this.hidePopup();
23556                         break;
23557                 }
23558                 break;
23559             case 'span':
23560                 if (className.indexOf('disabled') < 0) {
23561                 if (!this.viewDate) {
23562                     this.viewDate = new Date();
23563                 }
23564                 this.viewDate.setUTCDate(1);
23565                     if (className.indexOf('month') > -1) {
23566                         this.viewDate.setUTCMonth(Roo.bootstrap.form.DateField.dates[this.language].monthsShort.indexOf(html));
23567                     } else {
23568                         var year = parseInt(html, 10) || 0;
23569                         this.viewDate.setUTCFullYear(year);
23570                         
23571                     }
23572                     
23573                     if(this.singleMode){
23574                         this.setValue(this.formatDate(this.viewDate));
23575                         this.hidePopup();
23576                         return;
23577                     }
23578                     
23579                     this.showMode(-1);
23580                     this.fill();
23581                 }
23582                 break;
23583                 
23584             case 'td':
23585                 //Roo.log(className);
23586                 if (className.indexOf('day') > -1 && className.indexOf('disabled') < 0 ){
23587                     var day = parseInt(html, 10) || 1;
23588                     var year =  (this.viewDate || new Date()).getUTCFullYear(),
23589                         month = (this.viewDate || new Date()).getUTCMonth();
23590
23591                     if (className.indexOf('old') > -1) {
23592                         if(month === 0 ){
23593                             month = 11;
23594                             year -= 1;
23595                         }else{
23596                             month -= 1;
23597                         }
23598                     } else if (className.indexOf('new') > -1) {
23599                         if (month == 11) {
23600                             month = 0;
23601                             year += 1;
23602                         } else {
23603                             month += 1;
23604                         }
23605                     }
23606                     //Roo.log([year,month,day]);
23607                     this.date = this.UTCDate(year, month, day,0,0,0,0);
23608                     this.viewDate = this.UTCDate(year, month, Math.min(28, day),0,0,0,0);
23609 //                    this.fill();
23610                     //Roo.log(this.formatDate(this.date));
23611                     this.setValue(this.formatDate(this.date));
23612                     this.hidePopup();
23613                 }
23614                 break;
23615         }
23616     },
23617     
23618     setStartDate: function(startDate)
23619     {
23620         this.startDate = startDate || -Infinity;
23621         if (this.startDate !== -Infinity) {
23622             this.startDate = this.parseDate(this.startDate);
23623         }
23624         this.update();
23625         this.updateNavArrows();
23626     },
23627
23628     setEndDate: function(endDate)
23629     {
23630         this.endDate = endDate || Infinity;
23631         if (this.endDate !== Infinity) {
23632             this.endDate = this.parseDate(this.endDate);
23633         }
23634         this.update();
23635         this.updateNavArrows();
23636     },
23637     
23638     setDaysOfWeekDisabled: function(daysOfWeekDisabled)
23639     {
23640         this.daysOfWeekDisabled = daysOfWeekDisabled || [];
23641         if (typeof(this.daysOfWeekDisabled) !== 'object') {
23642             this.daysOfWeekDisabled = this.daysOfWeekDisabled.split(/,\s*/);
23643         }
23644         this.daysOfWeekDisabled = this.daysOfWeekDisabled.map(function (d) {
23645             return parseInt(d, 10);
23646         });
23647         this.update();
23648         this.updateNavArrows();
23649     },
23650     
23651     updateNavArrows: function() 
23652     {
23653         if(this.singleMode){
23654             return;
23655         }
23656         
23657         var d = new Date(this.viewDate),
23658         year = d.getUTCFullYear(),
23659         month = d.getUTCMonth();
23660         
23661         Roo.each(this.picker().select('.prev', true).elements, function(v){
23662             v.show();
23663             switch (this.viewMode) {
23664                 case 0:
23665
23666                     if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear() && month <= this.startDate.getUTCMonth()) {
23667                         v.hide();
23668                     }
23669                     break;
23670                 case 1:
23671                 case 2:
23672                     if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear()) {
23673                         v.hide();
23674                     }
23675                     break;
23676             }
23677         });
23678         
23679         Roo.each(this.picker().select('.next', true).elements, function(v){
23680             v.show();
23681             switch (this.viewMode) {
23682                 case 0:
23683
23684                     if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear() && month >= this.endDate.getUTCMonth()) {
23685                         v.hide();
23686                     }
23687                     break;
23688                 case 1:
23689                 case 2:
23690                     if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear()) {
23691                         v.hide();
23692                     }
23693                     break;
23694             }
23695         })
23696     },
23697     
23698     moveMonth: function(date, dir)
23699     {
23700         if (!dir) {
23701             return date;
23702         }
23703         var new_date = new Date(date.valueOf()),
23704         day = new_date.getUTCDate(),
23705         month = new_date.getUTCMonth(),
23706         mag = Math.abs(dir),
23707         new_month, test;
23708         dir = dir > 0 ? 1 : -1;
23709         if (mag == 1){
23710             test = dir == -1
23711             // If going back one month, make sure month is not current month
23712             // (eg, Mar 31 -> Feb 31 == Feb 28, not Mar 02)
23713             ? function(){
23714                 return new_date.getUTCMonth() == month;
23715             }
23716             // If going forward one month, make sure month is as expected
23717             // (eg, Jan 31 -> Feb 31 == Feb 28, not Mar 02)
23718             : function(){
23719                 return new_date.getUTCMonth() != new_month;
23720             };
23721             new_month = month + dir;
23722             new_date.setUTCMonth(new_month);
23723             // Dec -> Jan (12) or Jan -> Dec (-1) -- limit expected date to 0-11
23724             if (new_month < 0 || new_month > 11) {
23725                 new_month = (new_month + 12) % 12;
23726             }
23727         } else {
23728             // For magnitudes >1, move one month at a time...
23729             for (var i=0; i<mag; i++) {
23730                 // ...which might decrease the day (eg, Jan 31 to Feb 28, etc)...
23731                 new_date = this.moveMonth(new_date, dir);
23732             }
23733             // ...then reset the day, keeping it in the new month
23734             new_month = new_date.getUTCMonth();
23735             new_date.setUTCDate(day);
23736             test = function(){
23737                 return new_month != new_date.getUTCMonth();
23738             };
23739         }
23740         // Common date-resetting loop -- if date is beyond end of month, make it
23741         // end of month
23742         while (test()){
23743             new_date.setUTCDate(--day);
23744             new_date.setUTCMonth(new_month);
23745         }
23746         return new_date;
23747     },
23748
23749     moveYear: function(date, dir)
23750     {
23751         return this.moveMonth(date, dir*12);
23752     },
23753
23754     dateWithinRange: function(date)
23755     {
23756         return date >= this.startDate && date <= this.endDate;
23757     },
23758
23759     
23760     remove: function() 
23761     {
23762         this.picker().remove();
23763     },
23764     
23765     validateValue : function(value)
23766     {
23767         if(this.getVisibilityEl().hasClass('hidden')){
23768             return true;
23769         }
23770         
23771         if(value.length < 1)  {
23772             if(this.allowBlank){
23773                 return true;
23774             }
23775             return false;
23776         }
23777         
23778         if(value.length < this.minLength){
23779             return false;
23780         }
23781         if(value.length > this.maxLength){
23782             return false;
23783         }
23784         if(this.vtype){
23785             var vt = Roo.form.VTypes;
23786             if(!vt[this.vtype](value, this)){
23787                 return false;
23788             }
23789         }
23790         if(typeof this.validator == "function"){
23791             var msg = this.validator(value);
23792             if(msg !== true){
23793                 return false;
23794             }
23795         }
23796         
23797         if(this.regex && !this.regex.test(value)){
23798             return false;
23799         }
23800         
23801         if(typeof(this.parseDate(value)) == 'undefined'){
23802             return false;
23803         }
23804         
23805         if (this.endDate !== Infinity && this.parseDate(value).getTime() > this.endDate.getTime()) {
23806             return false;
23807         }      
23808         
23809         if (this.startDate !== -Infinity && this.parseDate(value).getTime() < this.startDate.getTime()) {
23810             return false;
23811         } 
23812         
23813         
23814         return true;
23815     },
23816     
23817     reset : function()
23818     {
23819         this.date = this.viewDate = '';
23820         
23821         Roo.bootstrap.form.DateField.superclass.setValue.call(this, '');
23822     }
23823    
23824 });
23825
23826 Roo.apply(Roo.bootstrap.form.DateField,  {
23827     
23828     head : {
23829         tag: 'thead',
23830         cn: [
23831         {
23832             tag: 'tr',
23833             cn: [
23834             {
23835                 tag: 'th',
23836                 cls: 'prev',
23837                 html: '<i class="fa fa-arrow-left"/>'
23838             },
23839             {
23840                 tag: 'th',
23841                 cls: 'switch',
23842                 colspan: '5'
23843             },
23844             {
23845                 tag: 'th',
23846                 cls: 'next',
23847                 html: '<i class="fa fa-arrow-right"/>'
23848             }
23849
23850             ]
23851         }
23852         ]
23853     },
23854     
23855     content : {
23856         tag: 'tbody',
23857         cn: [
23858         {
23859             tag: 'tr',
23860             cn: [
23861             {
23862                 tag: 'td',
23863                 colspan: '7'
23864             }
23865             ]
23866         }
23867         ]
23868     },
23869     
23870     footer : {
23871         tag: 'tfoot',
23872         cn: [
23873         {
23874             tag: 'tr',
23875             cn: [
23876             {
23877                 tag: 'th',
23878                 colspan: '7',
23879                 cls: 'today'
23880             }
23881                     
23882             ]
23883         }
23884         ]
23885     },
23886     
23887     dates:{
23888         en: {
23889             days: ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday"],
23890             daysShort: ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"],
23891             daysMin: ["Su", "Mo", "Tu", "We", "Th", "Fr", "Sa", "Su"],
23892             months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
23893             monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"],
23894             today: "Today"
23895         }
23896     },
23897     
23898     modes: [
23899     {
23900         clsName: 'days',
23901         navFnc: 'Month',
23902         navStep: 1
23903     },
23904     {
23905         clsName: 'months',
23906         navFnc: 'FullYear',
23907         navStep: 1
23908     },
23909     {
23910         clsName: 'years',
23911         navFnc: 'FullYear',
23912         navStep: 10
23913     }]
23914 });
23915
23916 Roo.apply(Roo.bootstrap.form.DateField,  {
23917   
23918     template : {
23919         tag: 'div',
23920         cls: 'datepicker dropdown-menu roo-dynamic shadow',
23921         cn: [
23922         {
23923             tag: 'div',
23924             cls: 'datepicker-days',
23925             cn: [
23926             {
23927                 tag: 'table',
23928                 cls: 'table-condensed',
23929                 cn:[
23930                 Roo.bootstrap.form.DateField.head,
23931                 {
23932                     tag: 'tbody'
23933                 },
23934                 Roo.bootstrap.form.DateField.footer
23935                 ]
23936             }
23937             ]
23938         },
23939         {
23940             tag: 'div',
23941             cls: 'datepicker-months',
23942             cn: [
23943             {
23944                 tag: 'table',
23945                 cls: 'table-condensed',
23946                 cn:[
23947                 Roo.bootstrap.form.DateField.head,
23948                 Roo.bootstrap.form.DateField.content,
23949                 Roo.bootstrap.form.DateField.footer
23950                 ]
23951             }
23952             ]
23953         },
23954         {
23955             tag: 'div',
23956             cls: 'datepicker-years',
23957             cn: [
23958             {
23959                 tag: 'table',
23960                 cls: 'table-condensed',
23961                 cn:[
23962                 Roo.bootstrap.form.DateField.head,
23963                 Roo.bootstrap.form.DateField.content,
23964                 Roo.bootstrap.form.DateField.footer
23965                 ]
23966             }
23967             ]
23968         }
23969         ]
23970     }
23971 });
23972
23973  
23974
23975  /*
23976  * - LGPL
23977  *
23978  * TimeField
23979  * 
23980  */
23981
23982 /**
23983  * @class Roo.bootstrap.form.TimeField
23984  * @extends Roo.bootstrap.form.Input
23985  * Bootstrap DateField class
23986  * 
23987  * 
23988  * @constructor
23989  * Create a new TimeField
23990  * @param {Object} config The config object
23991  */
23992
23993 Roo.bootstrap.form.TimeField = function(config){
23994     Roo.bootstrap.form.TimeField.superclass.constructor.call(this, config);
23995     this.addEvents({
23996             /**
23997              * @event show
23998              * Fires when this field show.
23999              * @param {Roo.bootstrap.form.DateField} thisthis
24000              * @param {Mixed} date The date value
24001              */
24002             show : true,
24003             /**
24004              * @event show
24005              * Fires when this field hide.
24006              * @param {Roo.bootstrap.form.DateField} this
24007              * @param {Mixed} date The date value
24008              */
24009             hide : true,
24010             /**
24011              * @event select
24012              * Fires when select a date.
24013              * @param {Roo.bootstrap.form.DateField} this
24014              * @param {Mixed} date The date value
24015              */
24016             select : true
24017         });
24018 };
24019
24020 Roo.extend(Roo.bootstrap.form.TimeField, Roo.bootstrap.form.Input,  {
24021     
24022     /**
24023      * @cfg {String} format
24024      * The default time format string which can be overriden for localization support.  The format must be
24025      * valid according to {@link Date#parseDate} (defaults to 'H:i').
24026      */
24027     format : "H:i",
24028
24029     getAutoCreate : function()
24030     {
24031         this.after = '<i class="fa far fa-clock"></i>';
24032         return Roo.bootstrap.form.TimeField.superclass.getAutoCreate.call(this);
24033         
24034          
24035     },
24036     onRender: function(ct, position)
24037     {
24038         
24039         Roo.bootstrap.form.TimeField.superclass.onRender.call(this, ct, position);
24040                 
24041         this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.form.TimeField.template);
24042         
24043         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
24044         
24045         this.pop = this.picker().select('>.datepicker-time',true).first();
24046         this.pop.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
24047         
24048         this.picker().on('mousedown', this.onMousedown, this);
24049         this.picker().on('click', this.onClick, this);
24050         
24051         this.picker().addClass('datepicker-dropdown');
24052     
24053         this.fillTime();
24054         this.update();
24055             
24056         this.pop.select('.hours-up', true).first().on('click', this.onIncrementHours, this);
24057         this.pop.select('.hours-down', true).first().on('click', this.onDecrementHours, this);
24058         this.pop.select('.minutes-up', true).first().on('click', this.onIncrementMinutes, this);
24059         this.pop.select('.minutes-down', true).first().on('click', this.onDecrementMinutes, this);
24060         this.pop.select('button.period', true).first().on('click', this.onTogglePeriod, this);
24061         this.pop.select('button.ok', true).first().on('click', this.setTime, this);
24062
24063     },
24064     
24065     fireKey: function(e){
24066         if (!this.picker().isVisible()){
24067             if (e.keyCode == 27) { // allow escape to hide and re-show picker
24068                 this.show();
24069             }
24070             return;
24071         }
24072
24073         e.preventDefault();
24074         
24075         switch(e.keyCode){
24076             case 27: // escape
24077                 this.hide();
24078                 break;
24079             case 37: // left
24080             case 39: // right
24081                 this.onTogglePeriod();
24082                 break;
24083             case 38: // up
24084                 this.onIncrementMinutes();
24085                 break;
24086             case 40: // down
24087                 this.onDecrementMinutes();
24088                 break;
24089             case 13: // enter
24090             case 9: // tab
24091                 this.setTime();
24092                 break;
24093         }
24094     },
24095     
24096     onClick: function(e) {
24097         e.stopPropagation();
24098         e.preventDefault();
24099     },
24100     
24101     picker : function()
24102     {
24103         return this.pickerEl;
24104     },
24105     
24106     fillTime: function()
24107     {    
24108         var time = this.pop.select('tbody', true).first();
24109         
24110         time.dom.innerHTML = '';
24111         
24112         time.createChild({
24113             tag: 'tr',
24114             cn: [
24115                 {
24116                     tag: 'td',
24117                     cn: [
24118                         {
24119                             tag: 'a',
24120                             href: '#',
24121                             cls: 'btn',
24122                             cn: [
24123                                 {
24124                                     tag: 'i',
24125                                     cls: 'hours-up fa fas fa-chevron-up'
24126                                 }
24127                             ]
24128                         } 
24129                     ]
24130                 },
24131                 {
24132                     tag: 'td',
24133                     cls: 'separator'
24134                 },
24135                 {
24136                     tag: 'td',
24137                     cn: [
24138                         {
24139                             tag: 'a',
24140                             href: '#',
24141                             cls: 'btn',
24142                             cn: [
24143                                 {
24144                                     tag: 'i',
24145                                     cls: 'minutes-up fa fas fa-chevron-up'
24146                                 }
24147                             ]
24148                         }
24149                     ]
24150                 },
24151                 {
24152                     tag: 'td',
24153                     cls: 'separator'
24154                 }
24155             ]
24156         });
24157         
24158         time.createChild({
24159             tag: 'tr',
24160             cn: [
24161                 {
24162                     tag: 'td',
24163                     cn: [
24164                         {
24165                             tag: 'span',
24166                             cls: 'timepicker-hour',
24167                             html: '00'
24168                         }  
24169                     ]
24170                 },
24171                 {
24172                     tag: 'td',
24173                     cls: 'separator',
24174                     html: ':'
24175                 },
24176                 {
24177                     tag: 'td',
24178                     cn: [
24179                         {
24180                             tag: 'span',
24181                             cls: 'timepicker-minute',
24182                             html: '00'
24183                         }  
24184                     ]
24185                 },
24186                 {
24187                     tag: 'td',
24188                     cls: 'separator'
24189                 },
24190                 {
24191                     tag: 'td',
24192                     cn: [
24193                         {
24194                             tag: 'button',
24195                             type: 'button',
24196                             cls: 'btn btn-primary period',
24197                             html: 'AM'
24198                             
24199                         }
24200                     ]
24201                 }
24202             ]
24203         });
24204         
24205         time.createChild({
24206             tag: 'tr',
24207             cn: [
24208                 {
24209                     tag: 'td',
24210                     cn: [
24211                         {
24212                             tag: 'a',
24213                             href: '#',
24214                             cls: 'btn',
24215                             cn: [
24216                                 {
24217                                     tag: 'span',
24218                                     cls: 'hours-down fa fas fa-chevron-down'
24219                                 }
24220                             ]
24221                         }
24222                     ]
24223                 },
24224                 {
24225                     tag: 'td',
24226                     cls: 'separator'
24227                 },
24228                 {
24229                     tag: 'td',
24230                     cn: [
24231                         {
24232                             tag: 'a',
24233                             href: '#',
24234                             cls: 'btn',
24235                             cn: [
24236                                 {
24237                                     tag: 'span',
24238                                     cls: 'minutes-down fa fas fa-chevron-down'
24239                                 }
24240                             ]
24241                         }
24242                     ]
24243                 },
24244                 {
24245                     tag: 'td',
24246                     cls: 'separator'
24247                 }
24248             ]
24249         });
24250         
24251     },
24252     
24253     update: function()
24254     {
24255         
24256         this.time = (typeof(this.time) === 'undefined') ? new Date() : this.time;
24257         
24258         this.fill();
24259     },
24260     
24261     fill: function() 
24262     {
24263         var hours = this.time.getHours();
24264         var minutes = this.time.getMinutes();
24265         var period = 'AM';
24266         
24267         if(hours > 11){
24268             period = 'PM';
24269         }
24270         
24271         if(hours == 0){
24272             hours = 12;
24273         }
24274         
24275         
24276         if(hours > 12){
24277             hours = hours - 12;
24278         }
24279         
24280         if(hours < 10){
24281             hours = '0' + hours;
24282         }
24283         
24284         if(minutes < 10){
24285             minutes = '0' + minutes;
24286         }
24287         
24288         this.pop.select('.timepicker-hour', true).first().dom.innerHTML = hours;
24289         this.pop.select('.timepicker-minute', true).first().dom.innerHTML = minutes;
24290         this.pop.select('button', true).first().dom.innerHTML = period;
24291         
24292     },
24293     
24294     place: function()
24295     {   
24296         this.picker().removeClass(['bottom-left', 'bottom-right', 'top-left', 'top-right']);
24297         
24298         var cls = ['bottom'];
24299         
24300         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){ // top
24301             cls.pop();
24302             cls.push('top');
24303         }
24304         
24305         cls.push('right');
24306         
24307         if((Roo.lib.Dom.getViewWidth() + Roo.get(document.body).getScroll().left) - (this.inputEl().getLeft() + this.picker().getWidth()) < 0){ // left
24308             cls.pop();
24309             cls.push('left');
24310         }
24311         //this.picker().setXY(20000,20000);
24312         this.picker().addClass(cls.join('-'));
24313         
24314         var _this = this;
24315         
24316         Roo.each(cls, function(c){
24317             if(c == 'bottom'){
24318                 (function() {
24319                  //  
24320                 }).defer(200);
24321                  _this.picker().alignTo(_this.inputEl(),   "tr-br", [0, 10], false);
24322                 //_this.picker().setTop(_this.inputEl().getHeight());
24323                 return;
24324             }
24325             if(c == 'top'){
24326                  _this.picker().alignTo(_this.inputEl(),   "br-tr", [0, 10], false);
24327                 
24328                 //_this.picker().setTop(0 - _this.picker().getHeight());
24329                 return;
24330             }
24331             /*
24332             if(c == 'left'){
24333                 _this.picker().setLeft(_this.inputEl().getLeft() + _this.inputEl().getWidth() - _this.el.getLeft() - _this.picker().getWidth());
24334                 return;
24335             }
24336             if(c == 'right'){
24337                 _this.picker().setLeft(_this.inputEl().getLeft() - _this.el.getLeft());
24338                 return;
24339             }
24340             */
24341         });
24342         
24343     },
24344   
24345     onFocus : function()
24346     {
24347         Roo.bootstrap.form.TimeField.superclass.onFocus.call(this);
24348         this.show();
24349     },
24350     
24351     onBlur : function()
24352     {
24353         Roo.bootstrap.form.TimeField.superclass.onBlur.call(this);
24354         this.hide();
24355     },
24356     
24357     show : function()
24358     {
24359         this.picker().show();
24360         this.pop.show();
24361         this.update();
24362         this.place();
24363         
24364         this.fireEvent('show', this, this.date);
24365     },
24366     
24367     hide : function()
24368     {
24369         this.picker().hide();
24370         this.pop.hide();
24371         
24372         this.fireEvent('hide', this, this.date);
24373     },
24374     
24375     setTime : function()
24376     {
24377         this.hide();
24378         this.setValue(this.time.format(this.format));
24379         
24380         this.fireEvent('select', this, this.date);
24381         
24382         
24383     },
24384     
24385     onMousedown: function(e){
24386         e.stopPropagation();
24387         e.preventDefault();
24388     },
24389     
24390     onIncrementHours: function()
24391     {
24392         Roo.log('onIncrementHours');
24393         this.time = this.time.add(Date.HOUR, 1);
24394         this.update();
24395         
24396     },
24397     
24398     onDecrementHours: function()
24399     {
24400         Roo.log('onDecrementHours');
24401         this.time = this.time.add(Date.HOUR, -1);
24402         this.update();
24403     },
24404     
24405     onIncrementMinutes: function()
24406     {
24407         Roo.log('onIncrementMinutes');
24408         this.time = this.time.add(Date.MINUTE, 1);
24409         this.update();
24410     },
24411     
24412     onDecrementMinutes: function()
24413     {
24414         Roo.log('onDecrementMinutes');
24415         this.time = this.time.add(Date.MINUTE, -1);
24416         this.update();
24417     },
24418     
24419     onTogglePeriod: function()
24420     {
24421         Roo.log('onTogglePeriod');
24422         this.time = this.time.add(Date.HOUR, 12);
24423         this.update();
24424     }
24425     
24426    
24427 });
24428  
24429
24430 Roo.apply(Roo.bootstrap.form.TimeField,  {
24431   
24432     template : {
24433         tag: 'div',
24434         cls: 'datepicker dropdown-menu',
24435         cn: [
24436             {
24437                 tag: 'div',
24438                 cls: 'datepicker-time',
24439                 cn: [
24440                 {
24441                     tag: 'table',
24442                     cls: 'table-condensed',
24443                     cn:[
24444                         {
24445                             tag: 'tbody',
24446                             cn: [
24447                                 {
24448                                     tag: 'tr',
24449                                     cn: [
24450                                     {
24451                                         tag: 'td',
24452                                         colspan: '7'
24453                                     }
24454                                     ]
24455                                 }
24456                             ]
24457                         },
24458                         {
24459                             tag: 'tfoot',
24460                             cn: [
24461                                 {
24462                                     tag: 'tr',
24463                                     cn: [
24464                                     {
24465                                         tag: 'th',
24466                                         colspan: '7',
24467                                         cls: '',
24468                                         cn: [
24469                                             {
24470                                                 tag: 'button',
24471                                                 cls: 'btn btn-info ok',
24472                                                 html: 'OK'
24473                                             }
24474                                         ]
24475                                     }
24476                     
24477                                     ]
24478                                 }
24479                             ]
24480                         }
24481                     ]
24482                 }
24483                 ]
24484             }
24485         ]
24486     }
24487 });
24488
24489  
24490
24491  /*
24492  * - LGPL
24493  *
24494  * MonthField
24495  * 
24496  */
24497
24498 /**
24499  * @class Roo.bootstrap.form.MonthField
24500  * @extends Roo.bootstrap.form.Input
24501  * Bootstrap MonthField class
24502  * 
24503  * @cfg {String} language default en
24504  * 
24505  * @constructor
24506  * Create a new MonthField
24507  * @param {Object} config The config object
24508  */
24509
24510 Roo.bootstrap.form.MonthField = function(config){
24511     Roo.bootstrap.form.MonthField.superclass.constructor.call(this, config);
24512     
24513     this.addEvents({
24514         /**
24515          * @event show
24516          * Fires when this field show.
24517          * @param {Roo.bootstrap.form.MonthField} this
24518          * @param {Mixed} date The date value
24519          */
24520         show : true,
24521         /**
24522          * @event show
24523          * Fires when this field hide.
24524          * @param {Roo.bootstrap.form.MonthField} this
24525          * @param {Mixed} date The date value
24526          */
24527         hide : true,
24528         /**
24529          * @event select
24530          * Fires when select a date.
24531          * @param {Roo.bootstrap.form.MonthField} this
24532          * @param {String} oldvalue The old value
24533          * @param {String} newvalue The new value
24534          */
24535         select : true
24536     });
24537 };
24538
24539 Roo.extend(Roo.bootstrap.form.MonthField, Roo.bootstrap.form.Input,  {
24540     
24541     onRender: function(ct, position)
24542     {
24543         
24544         Roo.bootstrap.form.MonthField.superclass.onRender.call(this, ct, position);
24545         
24546         this.language = this.language || 'en';
24547         this.language = this.language in Roo.bootstrap.form.MonthField.dates ? this.language : this.language.split('-')[0];
24548         this.language = this.language in Roo.bootstrap.form.MonthField.dates ? this.language : "en";
24549         
24550         this.isRTL = Roo.bootstrap.form.MonthField.dates[this.language].rtl || false;
24551         this.isInline = false;
24552         this.isInput = true;
24553         this.component = this.el.select('.add-on', true).first() || false;
24554         this.component = (this.component && this.component.length === 0) ? false : this.component;
24555         this.hasInput = this.component && this.inputEL().length;
24556         
24557         this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.form.MonthField.template);
24558         
24559         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
24560         
24561         this.picker().on('mousedown', this.onMousedown, this);
24562         this.picker().on('click', this.onClick, this);
24563         
24564         this.picker().addClass('datepicker-dropdown');
24565         
24566         Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
24567             v.setStyle('width', '189px');
24568         });
24569         
24570         this.fillMonths();
24571         
24572         this.update();
24573         
24574         if(this.isInline) {
24575             this.show();
24576         }
24577         
24578     },
24579     
24580     setValue: function(v, suppressEvent)
24581     {   
24582         var o = this.getValue();
24583         
24584         Roo.bootstrap.form.MonthField.superclass.setValue.call(this, v);
24585         
24586         this.update();
24587
24588         if(suppressEvent !== true){
24589             this.fireEvent('select', this, o, v);
24590         }
24591         
24592     },
24593     
24594     getValue: function()
24595     {
24596         return this.value;
24597     },
24598     
24599     onClick: function(e) 
24600     {
24601         e.stopPropagation();
24602         e.preventDefault();
24603         
24604         var target = e.getTarget();
24605         
24606         if(target.nodeName.toLowerCase() === 'i'){
24607             target = Roo.get(target).dom.parentNode;
24608         }
24609         
24610         var nodeName = target.nodeName;
24611         var className = target.className;
24612         var html = target.innerHTML;
24613         
24614         if(nodeName.toLowerCase() != 'span' || className.indexOf('disabled') > -1 || className.indexOf('month') == -1){
24615             return;
24616         }
24617         
24618         this.vIndex = Roo.bootstrap.form.MonthField.dates[this.language].monthsShort.indexOf(html);
24619         
24620         this.setValue(Roo.bootstrap.form.MonthField.dates[this.language].months[this.vIndex]);
24621         
24622         this.hide();
24623                         
24624     },
24625     
24626     picker : function()
24627     {
24628         return this.pickerEl;
24629     },
24630     
24631     fillMonths: function()
24632     {    
24633         var i = 0;
24634         var months = this.picker().select('>.datepicker-months td', true).first();
24635         
24636         months.dom.innerHTML = '';
24637         
24638         while (i < 12) {
24639             var month = {
24640                 tag: 'span',
24641                 cls: 'month',
24642                 html: Roo.bootstrap.form.MonthField.dates[this.language].monthsShort[i++]
24643             };
24644             
24645             months.createChild(month);
24646         }
24647         
24648     },
24649     
24650     update: function()
24651     {
24652         var _this = this;
24653         
24654         if(typeof(this.vIndex) == 'undefined' && this.value.length){
24655             this.vIndex = Roo.bootstrap.form.MonthField.dates[this.language].months.indexOf(this.value);
24656         }
24657         
24658         Roo.each(this.pickerEl.select('> .datepicker-months tbody > tr > td > span', true).elements, function(e, k){
24659             e.removeClass('active');
24660             
24661             if(typeof(_this.vIndex) != 'undefined' && k == _this.vIndex){
24662                 e.addClass('active');
24663             }
24664         })
24665     },
24666     
24667     place: function()
24668     {
24669         if(this.isInline) {
24670             return;
24671         }
24672         
24673         this.picker().removeClass(['bottom', 'top']);
24674         
24675         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
24676             /*
24677              * place to the top of element!
24678              *
24679              */
24680             
24681             this.picker().addClass('top');
24682             this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
24683             
24684             return;
24685         }
24686         
24687         this.picker().addClass('bottom');
24688         
24689         this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
24690     },
24691     
24692     onFocus : function()
24693     {
24694         Roo.bootstrap.form.MonthField.superclass.onFocus.call(this);
24695         this.show();
24696     },
24697     
24698     onBlur : function()
24699     {
24700         Roo.bootstrap.form.MonthField.superclass.onBlur.call(this);
24701         
24702         var d = this.inputEl().getValue();
24703         
24704         this.setValue(d);
24705                 
24706         this.hide();
24707     },
24708     
24709     show : function()
24710     {
24711         this.picker().show();
24712         this.picker().select('>.datepicker-months', true).first().show();
24713         this.update();
24714         this.place();
24715         
24716         this.fireEvent('show', this, this.date);
24717     },
24718     
24719     hide : function()
24720     {
24721         if(this.isInline) {
24722             return;
24723         }
24724         this.picker().hide();
24725         this.fireEvent('hide', this, this.date);
24726         
24727     },
24728     
24729     onMousedown: function(e)
24730     {
24731         e.stopPropagation();
24732         e.preventDefault();
24733     },
24734     
24735     keyup: function(e)
24736     {
24737         Roo.bootstrap.form.MonthField.superclass.keyup.call(this);
24738         this.update();
24739     },
24740
24741     fireKey: function(e)
24742     {
24743         if (!this.picker().isVisible()){
24744             if (e.keyCode == 27)   {// allow escape to hide and re-show picker
24745                 this.show();
24746             }
24747             return;
24748         }
24749         
24750         var dir;
24751         
24752         switch(e.keyCode){
24753             case 27: // escape
24754                 this.hide();
24755                 e.preventDefault();
24756                 break;
24757             case 37: // left
24758             case 39: // right
24759                 dir = e.keyCode == 37 ? -1 : 1;
24760                 
24761                 this.vIndex = this.vIndex + dir;
24762                 
24763                 if(this.vIndex < 0){
24764                     this.vIndex = 0;
24765                 }
24766                 
24767                 if(this.vIndex > 11){
24768                     this.vIndex = 11;
24769                 }
24770                 
24771                 if(isNaN(this.vIndex)){
24772                     this.vIndex = 0;
24773                 }
24774                 
24775                 this.setValue(Roo.bootstrap.form.MonthField.dates[this.language].months[this.vIndex]);
24776                 
24777                 break;
24778             case 38: // up
24779             case 40: // down
24780                 
24781                 dir = e.keyCode == 38 ? -1 : 1;
24782                 
24783                 this.vIndex = this.vIndex + dir * 4;
24784                 
24785                 if(this.vIndex < 0){
24786                     this.vIndex = 0;
24787                 }
24788                 
24789                 if(this.vIndex > 11){
24790                     this.vIndex = 11;
24791                 }
24792                 
24793                 if(isNaN(this.vIndex)){
24794                     this.vIndex = 0;
24795                 }
24796                 
24797                 this.setValue(Roo.bootstrap.form.MonthField.dates[this.language].months[this.vIndex]);
24798                 break;
24799                 
24800             case 13: // enter
24801                 
24802                 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
24803                     this.setValue(Roo.bootstrap.form.MonthField.dates[this.language].months[this.vIndex]);
24804                 }
24805                 
24806                 this.hide();
24807                 e.preventDefault();
24808                 break;
24809             case 9: // tab
24810                 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
24811                     this.setValue(Roo.bootstrap.form.MonthField.dates[this.language].months[this.vIndex]);
24812                 }
24813                 this.hide();
24814                 break;
24815             case 16: // shift
24816             case 17: // ctrl
24817             case 18: // alt
24818                 break;
24819             default :
24820                 this.hide();
24821                 
24822         }
24823     },
24824     
24825     remove: function() 
24826     {
24827         this.picker().remove();
24828     }
24829    
24830 });
24831
24832 Roo.apply(Roo.bootstrap.form.MonthField,  {
24833     
24834     content : {
24835         tag: 'tbody',
24836         cn: [
24837         {
24838             tag: 'tr',
24839             cn: [
24840             {
24841                 tag: 'td',
24842                 colspan: '7'
24843             }
24844             ]
24845         }
24846         ]
24847     },
24848     
24849     dates:{
24850         en: {
24851             months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
24852             monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"]
24853         }
24854     }
24855 });
24856
24857 Roo.apply(Roo.bootstrap.form.MonthField,  {
24858   
24859     template : {
24860         tag: 'div',
24861         cls: 'datepicker dropdown-menu roo-dynamic',
24862         cn: [
24863             {
24864                 tag: 'div',
24865                 cls: 'datepicker-months',
24866                 cn: [
24867                 {
24868                     tag: 'table',
24869                     cls: 'table-condensed',
24870                     cn:[
24871                         Roo.bootstrap.form.DateField.content
24872                     ]
24873                 }
24874                 ]
24875             }
24876         ]
24877     }
24878 });
24879
24880  
24881
24882  
24883  /*
24884  * - LGPL
24885  *
24886  * CheckBox
24887  * 
24888  */
24889
24890 /**
24891  * @class Roo.bootstrap.form.CheckBox
24892  * @extends Roo.bootstrap.form.Input
24893  * Bootstrap CheckBox class
24894  * 
24895  * @cfg {String} valueOff The value that should go into the generated input element's value when unchecked.
24896  * @cfg {String} inputValue The value that should go into the generated input element's value when checked.
24897  * @cfg {String} boxLabel The text that appears beside the checkbox
24898  * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the checkbox
24899  * @cfg {Boolean} checked initnal the element
24900  * @cfg {Boolean} inline inline the element (default false)
24901  * @cfg {String} groupId the checkbox group id // normal just use for checkbox
24902  * @cfg {String} tooltip label tooltip
24903  * 
24904  * @constructor
24905  * Create a new CheckBox
24906  * @param {Object} config The config object
24907  */
24908
24909 Roo.bootstrap.form.CheckBox = function(config){
24910     Roo.bootstrap.form.CheckBox.superclass.constructor.call(this, config);
24911    
24912     this.addEvents({
24913         /**
24914         * @event check
24915         * Fires when the element is checked or unchecked.
24916         * @param {Roo.bootstrap.form.CheckBox} this This input
24917         * @param {Boolean} checked The new checked value
24918         */
24919        check : true,
24920        /**
24921         * @event click
24922         * Fires when the element is click.
24923         * @param {Roo.bootstrap.form.CheckBox} this This input
24924         */
24925        click : true
24926     });
24927     
24928 };
24929
24930 Roo.extend(Roo.bootstrap.form.CheckBox, Roo.bootstrap.form.Input,  {
24931   
24932     inputType: 'checkbox',
24933     inputValue: 1,
24934     valueOff: 0,
24935     boxLabel: false,
24936     checked: false,
24937     weight : false,
24938     inline: false,
24939     tooltip : '',
24940     
24941     // checkbox success does not make any sense really.. 
24942     invalidClass : "",
24943     validClass : "",
24944     
24945     
24946     getAutoCreate : function()
24947     {
24948         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
24949         
24950         var id = Roo.id();
24951         
24952         var cfg = {};
24953         
24954         cfg.cls = 'form-group form-check ' + this.inputType; //input-group
24955         
24956         if(this.inline){
24957             cfg.cls += ' ' + this.inputType + '-inline  form-check-inline';
24958         }
24959         
24960         var input =  {
24961             tag: 'input',
24962             id : id,
24963             type : this.inputType,
24964             value : this.inputValue,
24965             cls : 'roo-' + this.inputType, //'form-box',
24966             placeholder : this.placeholder || ''
24967             
24968         };
24969         
24970         if(this.inputType != 'radio'){
24971             var hidden =  {
24972                 tag: 'input',
24973                 type : 'hidden',
24974                 cls : 'roo-hidden-value',
24975                 value : this.checked ? this.inputValue : this.valueOff
24976             };
24977         }
24978         
24979             
24980         if (this.weight) { // Validity check?
24981             cfg.cls += " " + this.inputType + "-" + this.weight;
24982         }
24983         
24984         if (this.disabled) {
24985             input.disabled=true;
24986         }
24987         
24988         if(this.checked){
24989             input.checked = this.checked;
24990         }
24991         
24992         if (this.name) {
24993             
24994             input.name = this.name;
24995             
24996             if(this.inputType != 'radio'){
24997                 hidden.name = this.name;
24998                 input.name = '_hidden_' + this.name;
24999             }
25000         }
25001         
25002         if (this.size) {
25003             input.cls += ' input-' + this.size;
25004         }
25005         
25006         var settings=this;
25007         
25008         ['xs','sm','md','lg'].map(function(size){
25009             if (settings[size]) {
25010                 cfg.cls += ' col-' + size + '-' + settings[size];
25011             }
25012         });
25013         
25014         var inputblock = input;
25015          
25016         if (this.before || this.after) {
25017             
25018             inputblock = {
25019                 cls : 'input-group',
25020                 cn :  [] 
25021             };
25022             
25023             if (this.before) {
25024                 inputblock.cn.push({
25025                     tag :'span',
25026                     cls : 'input-group-addon',
25027                     html : this.before
25028                 });
25029             }
25030             
25031             inputblock.cn.push(input);
25032             
25033             if(this.inputType != 'radio'){
25034                 inputblock.cn.push(hidden);
25035             }
25036             
25037             if (this.after) {
25038                 inputblock.cn.push({
25039                     tag :'span',
25040                     cls : 'input-group-addon',
25041                     html : this.after
25042                 });
25043             }
25044             
25045         }
25046         var boxLabelCfg = false;
25047         
25048         if(this.boxLabel){
25049            
25050             boxLabelCfg = {
25051                 tag: 'label',
25052                 //'for': id, // box label is handled by onclick - so no for...
25053                 cls: 'box-label',
25054                 html: this.boxLabel
25055             };
25056             if(this.tooltip){
25057                 boxLabelCfg.tooltip = this.tooltip;
25058             }
25059              
25060         }
25061         
25062         
25063         if (align ==='left' && this.fieldLabel.length) {
25064 //                Roo.log("left and has label");
25065             cfg.cn = [
25066                 {
25067                     tag: 'label',
25068                     'for' :  id,
25069                     cls : 'control-label',
25070                     html : this.fieldLabel
25071                 },
25072                 {
25073                     cls : "", 
25074                     cn: [
25075                         inputblock
25076                     ]
25077                 }
25078             ];
25079             
25080             if (boxLabelCfg) {
25081                 cfg.cn[1].cn.push(boxLabelCfg);
25082             }
25083             
25084             if(this.labelWidth > 12){
25085                 cfg.cn[0].style = "width: " + this.labelWidth + 'px';
25086             }
25087             
25088             if(this.labelWidth < 13 && this.labelmd == 0){
25089                 this.labelmd = this.labelWidth;
25090             }
25091             
25092             if(this.labellg > 0){
25093                 cfg.cn[0].cls += ' col-lg-' + this.labellg;
25094                 cfg.cn[1].cls += ' col-lg-' + (12 - this.labellg);
25095             }
25096             
25097             if(this.labelmd > 0){
25098                 cfg.cn[0].cls += ' col-md-' + this.labelmd;
25099                 cfg.cn[1].cls += ' col-md-' + (12 - this.labelmd);
25100             }
25101             
25102             if(this.labelsm > 0){
25103                 cfg.cn[0].cls += ' col-sm-' + this.labelsm;
25104                 cfg.cn[1].cls += ' col-sm-' + (12 - this.labelsm);
25105             }
25106             
25107             if(this.labelxs > 0){
25108                 cfg.cn[0].cls += ' col-xs-' + this.labelxs;
25109                 cfg.cn[1].cls += ' col-xs-' + (12 - this.labelxs);
25110             }
25111             
25112         } else if ( this.fieldLabel.length) {
25113 //                Roo.log(" label");
25114                 cfg.cn = [
25115                    
25116                     {
25117                         tag: this.boxLabel ? 'span' : 'label',
25118                         'for': id,
25119                         cls: 'control-label box-input-label',
25120                         //cls : 'input-group-addon',
25121                         html : this.fieldLabel
25122                     },
25123                     
25124                     inputblock
25125                     
25126                 ];
25127                 if (boxLabelCfg) {
25128                     cfg.cn.push(boxLabelCfg);
25129                 }
25130
25131         } else {
25132             
25133 //                Roo.log(" no label && no align");
25134                 cfg.cn = [  inputblock ] ;
25135                 if (boxLabelCfg) {
25136                     cfg.cn.push(boxLabelCfg);
25137                 }
25138
25139                 
25140         }
25141         
25142        
25143         
25144         if(this.inputType != 'radio'){
25145             cfg.cn.push(hidden);
25146         }
25147         
25148         return cfg;
25149         
25150     },
25151     
25152     /**
25153      * return the real input element.
25154      */
25155     inputEl: function ()
25156     {
25157         return this.el.select('input.roo-' + this.inputType,true).first();
25158     },
25159     hiddenEl: function ()
25160     {
25161         return this.el.select('input.roo-hidden-value',true).first();
25162     },
25163     
25164     labelEl: function()
25165     {
25166         return this.el.select('label.control-label',true).first();
25167     },
25168     /* depricated... */
25169     
25170     label: function()
25171     {
25172         return this.labelEl();
25173     },
25174     
25175     boxLabelEl: function()
25176     {
25177         return this.el.select('label.box-label',true).first();
25178     },
25179     
25180     initEvents : function()
25181     {
25182 //        Roo.bootstrap.form.CheckBox.superclass.initEvents.call(this);
25183         
25184         this.inputEl().on('click', this.onClick,  this);
25185         
25186         if (this.boxLabel) { 
25187             this.el.select('label.box-label',true).first().on('click', this.onClick,  this);
25188         }
25189         
25190         this.startValue = this.getValue();
25191         
25192         if(this.groupId){
25193             Roo.bootstrap.form.CheckBox.register(this);
25194         }
25195     },
25196     
25197     onClick : function(e)
25198     {   
25199         if(this.fireEvent('click', this, e) !== false){
25200             this.setChecked(!this.checked);
25201         }
25202         
25203     },
25204     
25205     setChecked : function(state,suppressEvent)
25206     {
25207         this.startValue = this.getValue();
25208
25209         if(this.inputType == 'radio'){
25210             
25211             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
25212                 e.dom.checked = false;
25213             });
25214             
25215             this.inputEl().dom.checked = true;
25216             
25217             this.inputEl().dom.value = this.inputValue;
25218             
25219             if(suppressEvent !== true){
25220                 this.fireEvent('check', this, true);
25221             }
25222             
25223             this.validate();
25224             
25225             return;
25226         }
25227         
25228         this.checked = state;
25229         
25230         this.inputEl().dom.checked = state;
25231         
25232         
25233         this.hiddenEl().dom.value = state ? this.inputValue : this.valueOff;
25234         
25235         if(suppressEvent !== true){
25236             this.fireEvent('check', this, state);
25237         }
25238         
25239         this.validate();
25240     },
25241     
25242     getValue : function()
25243     {
25244         if(this.inputType == 'radio'){
25245             return this.getGroupValue();
25246         }
25247         
25248         return this.hiddenEl().dom.value;
25249         
25250     },
25251     
25252     getGroupValue : function()
25253     {
25254         if(typeof(this.el.up('form').child('input[name='+this.name+']:checked', true)) == 'undefined'){
25255             return '';
25256         }
25257         
25258         return this.el.up('form').child('input[name='+this.name+']:checked', true).value;
25259     },
25260     
25261     setValue : function(v,suppressEvent)
25262     {
25263         if(this.inputType == 'radio'){
25264             this.setGroupValue(v, suppressEvent);
25265             return;
25266         }
25267         
25268         this.setChecked(((typeof(v) == 'undefined') ? this.checked : (String(v) === String(this.inputValue))), suppressEvent);
25269         
25270         this.validate();
25271     },
25272     
25273     setGroupValue : function(v, suppressEvent)
25274     {
25275         this.startValue = this.getValue();
25276         
25277         Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
25278             e.dom.checked = false;
25279             
25280             if(e.dom.value == v){
25281                 e.dom.checked = true;
25282             }
25283         });
25284         
25285         if(suppressEvent !== true){
25286             this.fireEvent('check', this, true);
25287         }
25288
25289         this.validate();
25290         
25291         return;
25292     },
25293     
25294     validate : function()
25295     {
25296         if(this.getVisibilityEl().hasClass('hidden')){
25297             return true;
25298         }
25299         
25300         if(
25301                 this.disabled || 
25302                 (this.inputType == 'radio' && this.validateRadio()) ||
25303                 (this.inputType == 'checkbox' && this.validateCheckbox())
25304         ){
25305             this.markValid();
25306             return true;
25307         }
25308         
25309         this.markInvalid();
25310         return false;
25311     },
25312     
25313     validateRadio : function()
25314     {
25315         if(this.getVisibilityEl().hasClass('hidden')){
25316             return true;
25317         }
25318         
25319         if(this.allowBlank){
25320             return true;
25321         }
25322         
25323         var valid = false;
25324         
25325         Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
25326             if(!e.dom.checked){
25327                 return;
25328             }
25329             
25330             valid = true;
25331             
25332             return false;
25333         });
25334         
25335         return valid;
25336     },
25337     
25338     validateCheckbox : function()
25339     {
25340         if(!this.groupId){
25341             return (this.getValue() == this.inputValue || this.allowBlank) ? true : false;
25342             //return (this.getValue() == this.inputValue) ? true : false;
25343         }
25344         
25345         var group = Roo.bootstrap.form.CheckBox.get(this.groupId);
25346         
25347         if(!group){
25348             return false;
25349         }
25350         
25351         var r = false;
25352         
25353         for(var i in group){
25354             if(group[i].el.isVisible(true)){
25355                 r = false;
25356                 break;
25357             }
25358             
25359             r = true;
25360         }
25361         
25362         for(var i in group){
25363             if(r){
25364                 break;
25365             }
25366             
25367             r = (group[i].getValue() == group[i].inputValue) ? true : false;
25368         }
25369         
25370         return r;
25371     },
25372     
25373     /**
25374      * Mark this field as valid
25375      */
25376     markValid : function()
25377     {
25378         var _this = this;
25379         
25380         this.fireEvent('valid', this);
25381         
25382         var label = Roo.bootstrap.form.FieldLabel.get(this.name + '-group');
25383         
25384         if(this.groupId){
25385             label = Roo.bootstrap.form.FieldLabel.get(this.groupId + '-group');
25386         }
25387         
25388         if(label){
25389             label.markValid();
25390         }
25391
25392         if(this.inputType == 'radio'){
25393             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
25394                 var fg = e.findParent('.form-group', false, true);
25395                 if (Roo.bootstrap.version == 3) {
25396                     fg.removeClass([_this.invalidClass, _this.validClass]);
25397                     fg.addClass(_this.validClass);
25398                 } else {
25399                     fg.removeClass(['is-valid', 'is-invalid']);
25400                     fg.addClass('is-valid');
25401                 }
25402             });
25403             
25404             return;
25405         }
25406
25407         if(!this.groupId){
25408             var fg = this.el.findParent('.form-group', false, true);
25409             if (Roo.bootstrap.version == 3) {
25410                 fg.removeClass([this.invalidClass, this.validClass]);
25411                 fg.addClass(this.validClass);
25412             } else {
25413                 fg.removeClass(['is-valid', 'is-invalid']);
25414                 fg.addClass('is-valid');
25415             }
25416             return;
25417         }
25418         
25419         var group = Roo.bootstrap.form.CheckBox.get(this.groupId);
25420         
25421         if(!group){
25422             return;
25423         }
25424         
25425         for(var i in group){
25426             var fg = group[i].el.findParent('.form-group', false, true);
25427             if (Roo.bootstrap.version == 3) {
25428                 fg.removeClass([this.invalidClass, this.validClass]);
25429                 fg.addClass(this.validClass);
25430             } else {
25431                 fg.removeClass(['is-valid', 'is-invalid']);
25432                 fg.addClass('is-valid');
25433             }
25434         }
25435     },
25436     
25437      /**
25438      * Mark this field as invalid
25439      * @param {String} msg The validation message
25440      */
25441     markInvalid : function(msg)
25442     {
25443         if(this.allowBlank){
25444             return;
25445         }
25446         
25447         var _this = this;
25448         
25449         this.fireEvent('invalid', this, msg);
25450         
25451         var label = Roo.bootstrap.form.FieldLabel.get(this.name + '-group');
25452         
25453         if(this.groupId){
25454             label = Roo.bootstrap.form.FieldLabel.get(this.groupId + '-group');
25455         }
25456         
25457         if(label){
25458             label.markInvalid();
25459         }
25460             
25461         if(this.inputType == 'radio'){
25462             
25463             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
25464                 var fg = e.findParent('.form-group', false, true);
25465                 if (Roo.bootstrap.version == 3) {
25466                     fg.removeClass([_this.invalidClass, _this.validClass]);
25467                     fg.addClass(_this.invalidClass);
25468                 } else {
25469                     fg.removeClass(['is-invalid', 'is-valid']);
25470                     fg.addClass('is-invalid');
25471                 }
25472             });
25473             
25474             return;
25475         }
25476         
25477         if(!this.groupId){
25478             var fg = this.el.findParent('.form-group', false, true);
25479             if (Roo.bootstrap.version == 3) {
25480                 fg.removeClass([_this.invalidClass, _this.validClass]);
25481                 fg.addClass(_this.invalidClass);
25482             } else {
25483                 fg.removeClass(['is-invalid', 'is-valid']);
25484                 fg.addClass('is-invalid');
25485             }
25486             return;
25487         }
25488         
25489         var group = Roo.bootstrap.form.CheckBox.get(this.groupId);
25490         
25491         if(!group){
25492             return;
25493         }
25494         
25495         for(var i in group){
25496             var fg = group[i].el.findParent('.form-group', false, true);
25497             if (Roo.bootstrap.version == 3) {
25498                 fg.removeClass([_this.invalidClass, _this.validClass]);
25499                 fg.addClass(_this.invalidClass);
25500             } else {
25501                 fg.removeClass(['is-invalid', 'is-valid']);
25502                 fg.addClass('is-invalid');
25503             }
25504         }
25505         
25506     },
25507     
25508     clearInvalid : function()
25509     {
25510         Roo.bootstrap.form.Input.prototype.clearInvalid.call(this);
25511         
25512         // this.el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
25513         
25514         var label = Roo.bootstrap.form.FieldLabel.get(this.name + '-group');
25515         
25516         if (label && label.iconEl) {
25517             label.iconEl.removeClass([ label.validClass, label.invalidClass ]);
25518             label.iconEl.removeClass(['is-invalid', 'is-valid']);
25519         }
25520     },
25521     
25522     disable : function()
25523     {
25524         if(this.inputType != 'radio'){
25525             Roo.bootstrap.form.CheckBox.superclass.disable.call(this);
25526             return;
25527         }
25528         
25529         var _this = this;
25530         
25531         if(this.rendered){
25532             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
25533                 _this.getActionEl().addClass(this.disabledClass);
25534                 e.dom.disabled = true;
25535             });
25536         }
25537         
25538         this.disabled = true;
25539         this.fireEvent("disable", this);
25540         return this;
25541     },
25542
25543     enable : function()
25544     {
25545         if(this.inputType != 'radio'){
25546             Roo.bootstrap.form.CheckBox.superclass.enable.call(this);
25547             return;
25548         }
25549         
25550         var _this = this;
25551         
25552         if(this.rendered){
25553             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
25554                 _this.getActionEl().removeClass(this.disabledClass);
25555                 e.dom.disabled = false;
25556             });
25557         }
25558         
25559         this.disabled = false;
25560         this.fireEvent("enable", this);
25561         return this;
25562     },
25563     
25564     setBoxLabel : function(v)
25565     {
25566         this.boxLabel = v;
25567         
25568         if(this.rendered){
25569             this.el.select('label.box-label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
25570         }
25571     }
25572
25573 });
25574
25575 Roo.apply(Roo.bootstrap.form.CheckBox, {
25576     
25577     groups: {},
25578     
25579      /**
25580     * register a CheckBox Group
25581     * @param {Roo.bootstrap.form.CheckBox} the CheckBox to add
25582     */
25583     register : function(checkbox)
25584     {
25585         if(typeof(this.groups[checkbox.groupId]) == 'undefined'){
25586             this.groups[checkbox.groupId] = {};
25587         }
25588         
25589         if(this.groups[checkbox.groupId].hasOwnProperty(checkbox.name)){
25590             return;
25591         }
25592         
25593         this.groups[checkbox.groupId][checkbox.name] = checkbox;
25594         
25595     },
25596     /**
25597     * fetch a CheckBox Group based on the group ID
25598     * @param {string} the group ID
25599     * @returns {Roo.bootstrap.form.CheckBox} the CheckBox group
25600     */
25601     get: function(groupId) {
25602         if (typeof(this.groups[groupId]) == 'undefined') {
25603             return false;
25604         }
25605         
25606         return this.groups[groupId] ;
25607     }
25608     
25609     
25610 });
25611 /*
25612  * - LGPL
25613  *
25614  * RadioItem
25615  * 
25616  */
25617
25618 /**
25619  * @class Roo.bootstrap.form.Radio
25620  * @extends Roo.bootstrap.Component
25621  * Bootstrap Radio class
25622  * @cfg {String} boxLabel - the label associated
25623  * @cfg {String} value - the value of radio
25624  * 
25625  * @constructor
25626  * Create a new Radio
25627  * @param {Object} config The config object
25628  */
25629 Roo.bootstrap.form.Radio = function(config){
25630     Roo.bootstrap.form.Radio.superclass.constructor.call(this, config);
25631     
25632 };
25633
25634 Roo.extend(Roo.bootstrap.form.Radio, Roo.bootstrap.Component, {
25635     
25636     boxLabel : '',
25637     
25638     value : '',
25639     
25640     getAutoCreate : function()
25641     {
25642         var cfg = {
25643             tag : 'div',
25644             cls : 'form-group radio',
25645             cn : [
25646                 {
25647                     tag : 'label',
25648                     cls : 'box-label',
25649                     html : this.boxLabel
25650                 }
25651             ]
25652         };
25653         
25654         return cfg;
25655     },
25656     
25657     initEvents : function() 
25658     {
25659         this.parent().register(this);
25660         
25661         this.el.on('click', this.onClick, this);
25662         
25663     },
25664     
25665     onClick : function(e)
25666     {
25667         if(this.parent().fireEvent('click', this.parent(), this, e) !== false){
25668             this.setChecked(true);
25669         }
25670     },
25671     
25672     setChecked : function(state, suppressEvent)
25673     {
25674         this.parent().setValue(this.value, suppressEvent);
25675         
25676     },
25677     
25678     setBoxLabel : function(v)
25679     {
25680         this.boxLabel = v;
25681         
25682         if(this.rendered){
25683             this.el.select('label.box-label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
25684         }
25685     }
25686     
25687 });
25688  
25689
25690  /*
25691  * - LGPL
25692  *
25693  * Input
25694  * 
25695  */
25696
25697 /**
25698  * @class Roo.bootstrap.form.SecurePass
25699  * @extends Roo.bootstrap.form.Input
25700  * Bootstrap SecurePass class
25701  *
25702  * 
25703  * @constructor
25704  * Create a new SecurePass
25705  * @param {Object} config The config object
25706  */
25707  
25708 Roo.bootstrap.form.SecurePass = function (config) {
25709     // these go here, so the translation tool can replace them..
25710     this.errors = {
25711         PwdEmpty: "Please type a password, and then retype it to confirm.",
25712         PwdShort: "Your password must be at least 6 characters long. Please type a different password.",
25713         PwdLong: "Your password can't contain more than 16 characters. Please type a different password.",
25714         PwdBadChar: "The password contains characters that aren't allowed. Please type a different password.",
25715         IDInPwd: "Your password can't include the part of your ID. Please type a different password.",
25716         FNInPwd: "Your password can't contain your first name. Please type a different password.",
25717         LNInPwd: "Your password can't contain your last name. Please type a different password.",
25718         TooWeak: "Your password is Too Weak."
25719     },
25720     this.meterLabel = "Password strength:";
25721     this.pwdStrengths = ["Too Weak", "Weak", "Medium", "Strong"];
25722     this.meterClass = [
25723         "roo-password-meter-tooweak", 
25724         "roo-password-meter-weak", 
25725         "roo-password-meter-medium", 
25726         "roo-password-meter-strong", 
25727         "roo-password-meter-grey"
25728     ];
25729     
25730     this.errors = {};
25731     
25732     Roo.bootstrap.form.SecurePass.superclass.constructor.call(this, config);
25733 }
25734
25735 Roo.extend(Roo.bootstrap.form.SecurePass, Roo.bootstrap.form.Input, {
25736     /**
25737      * @cfg {String/Object} errors A Error spec, or true for a default spec (defaults to
25738      * {
25739      *  PwdEmpty: "Please type a password, and then retype it to confirm.",
25740      *  PwdShort: "Your password must be at least 6 characters long. Please type a different password.",
25741      *  PwdLong: "Your password can't contain more than 16 characters. Please type a different password.",
25742      *  PwdBadChar: "The password contains characters that aren't allowed. Please type a different password.",
25743      *  IDInPwd: "Your password can't include the part of your ID. Please type a different password.",
25744      *  FNInPwd: "Your password can't contain your first name. Please type a different password.",
25745      *  LNInPwd: "Your password can't contain your last name. Please type a different password."
25746      * })
25747      */
25748     // private
25749     
25750     meterWidth: 300,
25751     errorMsg :'',    
25752     errors: false,
25753     imageRoot: '/',
25754     /**
25755      * @cfg {String/Object} Label for the strength meter (defaults to
25756      * 'Password strength:')
25757      */
25758     // private
25759     meterLabel: '',
25760     /**
25761      * @cfg {String/Object} pwdStrengths A pwdStrengths spec, or true for a default spec (defaults to
25762      * ['Weak', 'Medium', 'Strong'])
25763      */
25764     // private    
25765     pwdStrengths: false,    
25766     // private
25767     strength: 0,
25768     // private
25769     _lastPwd: null,
25770     // private
25771     kCapitalLetter: 0,
25772     kSmallLetter: 1,
25773     kDigit: 2,
25774     kPunctuation: 3,
25775     
25776     insecure: false,
25777     // private
25778     initEvents: function ()
25779     {
25780         Roo.bootstrap.form.SecurePass.superclass.initEvents.call(this);
25781
25782         if (this.el.is('input[type=password]') && Roo.isSafari) {
25783             this.el.on('keydown', this.SafariOnKeyDown, this);
25784         }
25785
25786         this.el.on('keyup', this.checkStrength, this, {buffer: 50});
25787     },
25788     // private
25789     onRender: function (ct, position)
25790     {
25791         Roo.bootstrap.form.SecurePass.superclass.onRender.call(this, ct, position);
25792         this.wrap = this.el.wrap({cls: 'x-form-field-wrap'});
25793         this.trigger = this.wrap.createChild({tag: 'div', cls: 'StrengthMeter ' + this.triggerClass});
25794
25795         this.trigger.createChild({
25796                    cn: [
25797                     {
25798                     //id: 'PwdMeter',
25799                     tag: 'div',
25800                     cls: 'roo-password-meter-grey col-xs-12',
25801                     style: {
25802                         //width: 0,
25803                         //width: this.meterWidth + 'px'                                                
25804                         }
25805                     },
25806                     {                            
25807                          cls: 'roo-password-meter-text'                          
25808                     }
25809                 ]            
25810         });
25811
25812          
25813         if (this.hideTrigger) {
25814             this.trigger.setDisplayed(false);
25815         }
25816         this.setSize(this.width || '', this.height || '');
25817     },
25818     // private
25819     onDestroy: function ()
25820     {
25821         if (this.trigger) {
25822             this.trigger.removeAllListeners();
25823             this.trigger.remove();
25824         }
25825         if (this.wrap) {
25826             this.wrap.remove();
25827         }
25828         Roo.bootstrap.form.TriggerField.superclass.onDestroy.call(this);
25829     },
25830     // private
25831     checkStrength: function ()
25832     {
25833         var pwd = this.inputEl().getValue();
25834         if (pwd == this._lastPwd) {
25835             return;
25836         }
25837
25838         var strength;
25839         if (this.ClientSideStrongPassword(pwd)) {
25840             strength = 3;
25841         } else if (this.ClientSideMediumPassword(pwd)) {
25842             strength = 2;
25843         } else if (this.ClientSideWeakPassword(pwd)) {
25844             strength = 1;
25845         } else {
25846             strength = 0;
25847         }
25848         
25849         Roo.log('strength1: ' + strength);
25850         
25851         //var pm = this.trigger.child('div/div/div').dom;
25852         var pm = this.trigger.child('div/div');
25853         pm.removeClass(this.meterClass);
25854         pm.addClass(this.meterClass[strength]);
25855                 
25856         
25857         var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;        
25858                 
25859         pt.innerHTML = this.meterLabel + '&nbsp;' + this.pwdStrengths[strength];
25860         
25861         this._lastPwd = pwd;
25862     },
25863     reset: function ()
25864     {
25865         Roo.bootstrap.form.SecurePass.superclass.reset.call(this);
25866         
25867         this._lastPwd = '';
25868         
25869         var pm = this.trigger.child('div/div');
25870         pm.removeClass(this.meterClass);
25871         pm.addClass('roo-password-meter-grey');        
25872         
25873         
25874         var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;        
25875         
25876         pt.innerHTML = '';
25877         this.inputEl().dom.type='password';
25878     },
25879     // private
25880     validateValue: function (value)
25881     {
25882         if (!Roo.bootstrap.form.SecurePass.superclass.validateValue.call(this, value)) {
25883             return false;
25884         }
25885         if (value.length == 0) {
25886             if (this.allowBlank) {
25887                 this.clearInvalid();
25888                 return true;
25889             }
25890
25891             this.markInvalid(this.errors.PwdEmpty);
25892             this.errorMsg = this.errors.PwdEmpty;
25893             return false;
25894         }
25895         
25896         if(this.insecure){
25897             return true;
25898         }
25899         
25900         if (!value.match(/[\x21-\x7e]+/)) {
25901             this.markInvalid(this.errors.PwdBadChar);
25902             this.errorMsg = this.errors.PwdBadChar;
25903             return false;
25904         }
25905         if (value.length < 6) {
25906             this.markInvalid(this.errors.PwdShort);
25907             this.errorMsg = this.errors.PwdShort;
25908             return false;
25909         }
25910         if (value.length > 16) {
25911             this.markInvalid(this.errors.PwdLong);
25912             this.errorMsg = this.errors.PwdLong;
25913             return false;
25914         }
25915         var strength;
25916         if (this.ClientSideStrongPassword(value)) {
25917             strength = 3;
25918         } else if (this.ClientSideMediumPassword(value)) {
25919             strength = 2;
25920         } else if (this.ClientSideWeakPassword(value)) {
25921             strength = 1;
25922         } else {
25923             strength = 0;
25924         }
25925
25926         
25927         if (strength < 2) {
25928             //this.markInvalid(this.errors.TooWeak);
25929             this.errorMsg = this.errors.TooWeak;
25930             //return false;
25931         }
25932         
25933         
25934         console.log('strength2: ' + strength);
25935         
25936         //var pm = this.trigger.child('div/div/div').dom;
25937         
25938         var pm = this.trigger.child('div/div');
25939         pm.removeClass(this.meterClass);
25940         pm.addClass(this.meterClass[strength]);
25941                 
25942         var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;        
25943                 
25944         pt.innerHTML = this.meterLabel + '&nbsp;' + this.pwdStrengths[strength];
25945         
25946         this.errorMsg = ''; 
25947         return true;
25948     },
25949     // private
25950     CharacterSetChecks: function (type)
25951     {
25952         this.type = type;
25953         this.fResult = false;
25954     },
25955     // private
25956     isctype: function (character, type)
25957     {
25958         switch (type) {  
25959             case this.kCapitalLetter:
25960                 if (character >= 'A' && character <= 'Z') {
25961                     return true;
25962                 }
25963                 break;
25964             
25965             case this.kSmallLetter:
25966                 if (character >= 'a' && character <= 'z') {
25967                     return true;
25968                 }
25969                 break;
25970             
25971             case this.kDigit:
25972                 if (character >= '0' && character <= '9') {
25973                     return true;
25974                 }
25975                 break;
25976             
25977             case this.kPunctuation:
25978                 if ('!@#$%^&*()_+-=\'";:[{]}|.>,</?`~'.indexOf(character) >= 0) {
25979                     return true;
25980                 }
25981                 break;
25982             
25983             default:
25984                 return false;
25985         }
25986
25987     },
25988     // private
25989     IsLongEnough: function (pwd, size)
25990     {
25991         return !(pwd == null || isNaN(size) || pwd.length < size);
25992     },
25993     // private
25994     SpansEnoughCharacterSets: function (word, nb)
25995     {
25996         if (!this.IsLongEnough(word, nb))
25997         {
25998             return false;
25999         }
26000
26001         var characterSetChecks = new Array(
26002             new this.CharacterSetChecks(this.kCapitalLetter), new this.CharacterSetChecks(this.kSmallLetter),
26003             new this.CharacterSetChecks(this.kDigit), new this.CharacterSetChecks(this.kPunctuation)
26004         );
26005         
26006         for (var index = 0; index < word.length; ++index) {
26007             for (var nCharSet = 0; nCharSet < characterSetChecks.length; ++nCharSet) {
26008                 if (!characterSetChecks[nCharSet].fResult && this.isctype(word.charAt(index), characterSetChecks[nCharSet].type)) {
26009                     characterSetChecks[nCharSet].fResult = true;
26010                     break;
26011                 }
26012             }
26013         }
26014
26015         var nCharSets = 0;
26016         for (var nCharSet = 0; nCharSet < characterSetChecks.length; ++nCharSet) {
26017             if (characterSetChecks[nCharSet].fResult) {
26018                 ++nCharSets;
26019             }
26020         }
26021
26022         if (nCharSets < nb) {
26023             return false;
26024         }
26025         return true;
26026     },
26027     // private
26028     ClientSideStrongPassword: function (pwd)
26029     {
26030         return this.IsLongEnough(pwd, 8) && this.SpansEnoughCharacterSets(pwd, 3);
26031     },
26032     // private
26033     ClientSideMediumPassword: function (pwd)
26034     {
26035         return this.IsLongEnough(pwd, 7) && this.SpansEnoughCharacterSets(pwd, 2);
26036     },
26037     // private
26038     ClientSideWeakPassword: function (pwd)
26039     {
26040         return this.IsLongEnough(pwd, 6) || !this.IsLongEnough(pwd, 0);
26041     }
26042           
26043 });
26044 Roo.htmleditor = {};
26045  
26046 /**
26047  * @class Roo.htmleditor.Filter
26048  * Base Class for filtering htmleditor stuff. - do not use this directly - extend it.
26049  * @cfg {DomElement} node The node to iterate and filter
26050  * @cfg {boolean|String|Array} tag Tags to replace 
26051  * @constructor
26052  * Create a new Filter.
26053  * @param {Object} config Configuration options
26054  */
26055
26056
26057
26058 Roo.htmleditor.Filter = function(cfg) {
26059     Roo.apply(this.cfg);
26060     // this does not actually call walk as it's really just a abstract class
26061 }
26062
26063
26064 Roo.htmleditor.Filter.prototype = {
26065     
26066     node: false,
26067     
26068     tag: false,
26069
26070     // overrride to do replace comments.
26071     replaceComment : false,
26072     
26073     // overrride to do replace or do stuff with tags..
26074     replaceTag : false,
26075     
26076     walk : function(dom)
26077     {
26078         Roo.each( Array.from(dom.childNodes), function( e ) {
26079             switch(true) {
26080                 
26081                 case e.nodeType == 8 &&  this.replaceComment  !== false: // comment
26082                     this.replaceComment(e);
26083                     return;
26084                 
26085                 case e.nodeType != 1: //not a node.
26086                     return;
26087                 
26088                 case this.tag === true: // everything
26089                 case e.tagName.indexOf(":") > -1 && typeof(this.tag) == 'object' && this.tag.indexOf(":") > -1:
26090                 case e.tagName.indexOf(":") > -1 && typeof(this.tag) == 'string' && this.tag == ":":
26091                 case typeof(this.tag) == 'object' && this.tag.indexOf(e.tagName) > -1: // array and it matches.
26092                 case typeof(this.tag) == 'string' && this.tag == e.tagName: // array and it matches.
26093                     if (this.replaceTag && false === this.replaceTag(e)) {
26094                         return;
26095                     }
26096                     if (e.hasChildNodes()) {
26097                         this.walk(e);
26098                     }
26099                     return;
26100                 
26101                 default:    // tags .. that do not match.
26102                     if (e.hasChildNodes()) {
26103                         this.walk(e);
26104                     }
26105             }
26106             
26107         }, this);
26108         
26109     },
26110     
26111     
26112     removeNodeKeepChildren : function( node)
26113     {
26114     
26115         ar = Array.from(node.childNodes);
26116         for (var i = 0; i < ar.length; i++) {
26117          
26118             node.removeChild(ar[i]);
26119             // what if we need to walk these???
26120             node.parentNode.insertBefore(ar[i], node);
26121            
26122         }
26123         node.parentNode.removeChild(node);
26124     }
26125 }; 
26126
26127 /**
26128  * @class Roo.htmleditor.FilterAttributes
26129  * clean attributes and  styles including http:// etc.. in attribute
26130  * @constructor
26131 * Run a new Attribute Filter
26132 * @param {Object} config Configuration options
26133  */
26134 Roo.htmleditor.FilterAttributes = function(cfg)
26135 {
26136     Roo.apply(this, cfg);
26137     this.attrib_black = this.attrib_black || [];
26138     this.attrib_white = this.attrib_white || [];
26139
26140     this.attrib_clean = this.attrib_clean || [];
26141     this.style_white = this.style_white || [];
26142     this.style_black = this.style_black || [];
26143     this.walk(cfg.node);
26144 }
26145
26146 Roo.extend(Roo.htmleditor.FilterAttributes, Roo.htmleditor.Filter,
26147 {
26148     tag: true, // all tags
26149     
26150     attrib_black : false, // array
26151     attrib_clean : false,
26152     attrib_white : false,
26153
26154     style_white : false,
26155     style_black : false,
26156      
26157      
26158     replaceTag : function(node)
26159     {
26160         if (!node.attributes || !node.attributes.length) {
26161             return true;
26162         }
26163         
26164         for (var i = node.attributes.length-1; i > -1 ; i--) {
26165             var a = node.attributes[i];
26166             //console.log(a);
26167             if (this.attrib_white.length && this.attrib_white.indexOf(a.name.toLowerCase()) < 0) {
26168                 node.removeAttribute(a.name);
26169                 continue;
26170             }
26171             
26172             
26173             
26174             if (a.name.toLowerCase().substr(0,2)=='on')  {
26175                 node.removeAttribute(a.name);
26176                 continue;
26177             }
26178             
26179             
26180             if (this.attrib_black.indexOf(a.name.toLowerCase()) > -1) {
26181                 node.removeAttribute(a.name);
26182                 continue;
26183             }
26184             if (this.attrib_clean.indexOf(a.name.toLowerCase()) > -1) {
26185                 this.cleanAttr(node,a.name,a.value); // fixme..
26186                 continue;
26187             }
26188             if (a.name == 'style') {
26189                 this.cleanStyle(node,a.name,a.value);
26190                 continue;
26191             }
26192             /// clean up MS crap..
26193             // tecnically this should be a list of valid class'es..
26194             
26195             
26196             if (a.name == 'class') {
26197                 if (a.value.match(/^Mso/)) {
26198                     node.removeAttribute('class');
26199                 }
26200                 
26201                 if (a.value.match(/^body$/)) {
26202                     node.removeAttribute('class');
26203                 }
26204                 continue;
26205             }
26206             
26207             
26208             // style cleanup!?
26209             // class cleanup?
26210             
26211         }
26212         return true; // clean children
26213     },
26214         
26215     cleanAttr: function(node, n,v)
26216     {
26217         
26218         if (v.match(/^\./) || v.match(/^\//)) {
26219             return;
26220         }
26221         if (v.match(/^(http|https):\/\//)
26222             || v.match(/^mailto:/) 
26223             || v.match(/^ftp:/)
26224             || v.match(/^data:/)
26225             ) {
26226             return;
26227         }
26228         if (v.match(/^#/)) {
26229             return;
26230         }
26231         if (v.match(/^\{/)) { // allow template editing.
26232             return;
26233         }
26234 //            Roo.log("(REMOVE TAG)"+ node.tagName +'.' + n + '=' + v);
26235         node.removeAttribute(n);
26236         
26237     },
26238     cleanStyle : function(node,  n,v)
26239     {
26240         if (v.match(/expression/)) { //XSS?? should we even bother..
26241             node.removeAttribute(n);
26242             return;
26243         }
26244         
26245         var parts = v.split(/;/);
26246         var clean = [];
26247         
26248         Roo.each(parts, function(p) {
26249             p = p.replace(/^\s+/g,'').replace(/\s+$/g,'');
26250             if (!p.length) {
26251                 return true;
26252             }
26253             var l = p.split(':').shift().replace(/\s+/g,'');
26254             l = l.replace(/^\s+/g,'').replace(/\s+$/g,'');
26255             
26256             if ( this.style_black.length && (this.style_black.indexOf(l) > -1 || this.style_black.indexOf(l.toLowerCase()) > -1)) {
26257                 return true;
26258             }
26259             //Roo.log()
26260             // only allow 'c whitelisted system attributes'
26261             if ( this.style_white.length &&  style_white.indexOf(l) < 0 && style_white.indexOf(l.toLowerCase()) < 0 ) {
26262                 return true;
26263             }
26264             
26265             
26266             clean.push(p);
26267             return true;
26268         },this);
26269         if (clean.length) { 
26270             node.setAttribute(n, clean.join(';'));
26271         } else {
26272             node.removeAttribute(n);
26273         }
26274         
26275     }
26276         
26277         
26278         
26279     
26280 });/**
26281  * @class Roo.htmleditor.FilterBlack
26282  * remove blacklisted elements.
26283  * @constructor
26284  * Run a new Blacklisted Filter
26285  * @param {Object} config Configuration options
26286  */
26287
26288 Roo.htmleditor.FilterBlack = function(cfg)
26289 {
26290     Roo.apply(this, cfg);
26291     this.walk(cfg.node);
26292 }
26293
26294 Roo.extend(Roo.htmleditor.FilterBlack, Roo.htmleditor.Filter,
26295 {
26296     tag : true, // all elements.
26297    
26298     replaceTag : function(n)
26299     {
26300         n.parentNode.removeChild(n);
26301     }
26302 });
26303 /**
26304  * @class Roo.htmleditor.FilterComment
26305  * remove comments.
26306  * @constructor
26307 * Run a new Comments Filter
26308 * @param {Object} config Configuration options
26309  */
26310 Roo.htmleditor.FilterComment = function(cfg)
26311 {
26312     this.walk(cfg.node);
26313 }
26314
26315 Roo.extend(Roo.htmleditor.FilterComment, Roo.htmleditor.Filter,
26316 {
26317   
26318     replaceComment : function(n)
26319     {
26320         n.parentNode.removeChild(n);
26321     }
26322 });/**
26323  * @class Roo.htmleditor.FilterKeepChildren
26324  * remove tags but keep children
26325  * @constructor
26326  * Run a new Keep Children Filter
26327  * @param {Object} config Configuration options
26328  */
26329
26330 Roo.htmleditor.FilterKeepChildren = function(cfg)
26331 {
26332     Roo.apply(this, cfg);
26333     if (this.tag === false) {
26334         return; // dont walk.. (you can use this to use this just to do a child removal on a single tag )
26335     }
26336     // hacky?
26337     if ((typeof(this.tag) == 'object' && this.tag.indexOf(":") > -1)) {
26338         this.cleanNamespace = true;
26339     }
26340         
26341     this.walk(cfg.node);
26342 }
26343
26344 Roo.extend(Roo.htmleditor.FilterKeepChildren, Roo.htmleditor.FilterBlack,
26345 {
26346     cleanNamespace : false, // should really be an option, rather than using ':' inside of this tag.
26347   
26348     replaceTag : function(node)
26349     {
26350         // walk children...
26351         //Roo.log(node.tagName);
26352         var ar = Array.from(node.childNodes);
26353         //remove first..
26354         
26355         for (var i = 0; i < ar.length; i++) {
26356             var e = ar[i];
26357             if (e.nodeType == 1) {
26358                 if (
26359                     (typeof(this.tag) == 'object' && this.tag.indexOf(e.tagName) > -1)
26360                     || // array and it matches
26361                     (typeof(this.tag) == 'string' && this.tag == e.tagName)
26362                     ||
26363                     (e.tagName.indexOf(":") > -1 && typeof(this.tag) == 'object' && this.tag.indexOf(":") > -1)
26364                     ||
26365                     (e.tagName.indexOf(":") > -1 && typeof(this.tag) == 'string' && this.tag == ":")
26366                 ) {
26367                     this.replaceTag(ar[i]); // child is blacklisted as well...
26368                     continue;
26369                 }
26370             }
26371         }  
26372         ar = Array.from(node.childNodes);
26373         for (var i = 0; i < ar.length; i++) {
26374          
26375             node.removeChild(ar[i]);
26376             // what if we need to walk these???
26377             node.parentNode.insertBefore(ar[i], node);
26378             if (this.tag !== false) {
26379                 this.walk(ar[i]);
26380                 
26381             }
26382         }
26383         //Roo.log("REMOVE:" + node.tagName);
26384         node.parentNode.removeChild(node);
26385         return false; // don't walk children
26386         
26387         
26388     }
26389 });/**
26390  * @class Roo.htmleditor.FilterParagraph
26391  * paragraphs cause a nightmare for shared content - this filter is designed to be called ? at various points when editing
26392  * like on 'push' to remove the <p> tags and replace them with line breaks.
26393  * @constructor
26394  * Run a new Paragraph Filter
26395  * @param {Object} config Configuration options
26396  */
26397
26398 Roo.htmleditor.FilterParagraph = function(cfg)
26399 {
26400     // no need to apply config.
26401     this.walk(cfg.node);
26402 }
26403
26404 Roo.extend(Roo.htmleditor.FilterParagraph, Roo.htmleditor.Filter,
26405 {
26406     
26407      
26408     tag : 'P',
26409     
26410      
26411     replaceTag : function(node)
26412     {
26413         
26414         if (node.childNodes.length == 1 &&
26415             node.childNodes[0].nodeType == 3 &&
26416             node.childNodes[0].textContent.trim().length < 1
26417             ) {
26418             // remove and replace with '<BR>';
26419             node.parentNode.replaceChild(node.ownerDocument.createElement('BR'),node);
26420             return false; // no need to walk..
26421         }
26422         var ar = Array.from(node.childNodes);
26423         for (var i = 0; i < ar.length; i++) {
26424             node.removeChild(ar[i]);
26425             // what if we need to walk these???
26426             node.parentNode.insertBefore(ar[i], node);
26427         }
26428         // now what about this?
26429         // <p> &nbsp; </p>
26430         
26431         // double BR.
26432         node.parentNode.insertBefore(node.ownerDocument.createElement('BR'), node);
26433         node.parentNode.insertBefore(node.ownerDocument.createElement('BR'), node);
26434         node.parentNode.removeChild(node);
26435         
26436         return false;
26437
26438     }
26439     
26440 });/**
26441  * @class Roo.htmleditor.FilterSpan
26442  * filter span's with no attributes out..
26443  * @constructor
26444  * Run a new Span Filter
26445  * @param {Object} config Configuration options
26446  */
26447
26448 Roo.htmleditor.FilterSpan = function(cfg)
26449 {
26450     // no need to apply config.
26451     this.walk(cfg.node);
26452 }
26453
26454 Roo.extend(Roo.htmleditor.FilterSpan, Roo.htmleditor.FilterKeepChildren,
26455 {
26456      
26457     tag : 'SPAN',
26458      
26459  
26460     replaceTag : function(node)
26461     {
26462         if (node.attributes && node.attributes.length > 0) {
26463             return true; // walk if there are any.
26464         }
26465         Roo.htmleditor.FilterKeepChildren.prototype.replaceTag.call(this, node);
26466         return false;
26467      
26468     }
26469     
26470 });/**
26471  * @class Roo.htmleditor.FilterTableWidth
26472   try and remove table width data - as that frequently messes up other stuff.
26473  * 
26474  *      was cleanTableWidths.
26475  *
26476  * Quite often pasting from word etc.. results in tables with column and widths.
26477  * This does not work well on fluid HTML layouts - like emails. - so this code should hunt an destroy them..
26478  *
26479  * @constructor
26480  * Run a new Table Filter
26481  * @param {Object} config Configuration options
26482  */
26483
26484 Roo.htmleditor.FilterTableWidth = function(cfg)
26485 {
26486     // no need to apply config.
26487     this.tag = ['TABLE', 'TD', 'TR', 'TH', 'THEAD', 'TBODY' ];
26488     this.walk(cfg.node);
26489 }
26490
26491 Roo.extend(Roo.htmleditor.FilterTableWidth, Roo.htmleditor.Filter,
26492 {
26493      
26494      
26495     
26496     replaceTag: function(node) {
26497         
26498         
26499       
26500         if (node.hasAttribute('width')) {
26501             node.removeAttribute('width');
26502         }
26503         
26504          
26505         if (node.hasAttribute("style")) {
26506             // pretty basic...
26507             
26508             var styles = node.getAttribute("style").split(";");
26509             var nstyle = [];
26510             Roo.each(styles, function(s) {
26511                 if (!s.match(/:/)) {
26512                     return;
26513                 }
26514                 var kv = s.split(":");
26515                 if (kv[0].match(/^\s*(width|min-width)\s*$/)) {
26516                     return;
26517                 }
26518                 // what ever is left... we allow.
26519                 nstyle.push(s);
26520             });
26521             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
26522             if (!nstyle.length) {
26523                 node.removeAttribute('style');
26524             }
26525         }
26526         
26527         return true; // continue doing children..
26528     }
26529 });/**
26530  * @class Roo.htmleditor.FilterWord
26531  * try and clean up all the mess that Word generates.
26532  * 
26533  * This is the 'nice version' - see 'Heavy' that white lists a very short list of elements, and multi-filters 
26534  
26535  * @constructor
26536  * Run a new Span Filter
26537  * @param {Object} config Configuration options
26538  */
26539
26540 Roo.htmleditor.FilterWord = function(cfg)
26541 {
26542     // no need to apply config.
26543     this.replaceDocBullets(cfg.node);
26544     
26545     this.replaceAname(cfg.node);
26546     // this is disabled as the removal is done by other filters;
26547    // this.walk(cfg.node);
26548     
26549     
26550 }
26551
26552 Roo.extend(Roo.htmleditor.FilterWord, Roo.htmleditor.Filter,
26553 {
26554     tag: true,
26555      
26556     
26557     /**
26558      * Clean up MS wordisms...
26559      */
26560     replaceTag : function(node)
26561     {
26562          
26563         // no idea what this does - span with text, replaceds with just text.
26564         if(
26565                 node.nodeName == 'SPAN' &&
26566                 !node.hasAttributes() &&
26567                 node.childNodes.length == 1 &&
26568                 node.firstChild.nodeName == "#text"  
26569         ) {
26570             var textNode = node.firstChild;
26571             node.removeChild(textNode);
26572             if (node.getAttribute('lang') != 'zh-CN') {   // do not space pad on chinese characters..
26573                 node.parentNode.insertBefore(node.ownerDocument.createTextNode(" "), node);
26574             }
26575             node.parentNode.insertBefore(textNode, node);
26576             if (node.getAttribute('lang') != 'zh-CN') {   // do not space pad on chinese characters..
26577                 node.parentNode.insertBefore(node.ownerDocument.createTextNode(" ") , node);
26578             }
26579             
26580             node.parentNode.removeChild(node);
26581             return false; // dont do chidren - we have remove our node - so no need to do chdhilren?
26582         }
26583         
26584    
26585         
26586         if (node.tagName.toLowerCase().match(/^(style|script|applet|embed|noframes|noscript)$/)) {
26587             node.parentNode.removeChild(node);
26588             return false; // dont do chidlren
26589         }
26590         //Roo.log(node.tagName);
26591         // remove - but keep children..
26592         if (node.tagName.toLowerCase().match(/^(meta|link|\\?xml:|st1:|o:|v:|font)/)) {
26593             //Roo.log('-- removed');
26594             while (node.childNodes.length) {
26595                 var cn = node.childNodes[0];
26596                 node.removeChild(cn);
26597                 node.parentNode.insertBefore(cn, node);
26598                 // move node to parent - and clean it..
26599                 if (cn.nodeType == 1) {
26600                     this.replaceTag(cn);
26601                 }
26602                 
26603             }
26604             node.parentNode.removeChild(node);
26605             /// no need to iterate chidlren = it's got none..
26606             //this.iterateChildren(node, this.cleanWord);
26607             return false; // no need to iterate children.
26608         }
26609         // clean styles
26610         if (node.className.length) {
26611             
26612             var cn = node.className.split(/\W+/);
26613             var cna = [];
26614             Roo.each(cn, function(cls) {
26615                 if (cls.match(/Mso[a-zA-Z]+/)) {
26616                     return;
26617                 }
26618                 cna.push(cls);
26619             });
26620             node.className = cna.length ? cna.join(' ') : '';
26621             if (!cna.length) {
26622                 node.removeAttribute("class");
26623             }
26624         }
26625         
26626         if (node.hasAttribute("lang")) {
26627             node.removeAttribute("lang");
26628         }
26629         
26630         if (node.hasAttribute("style")) {
26631             
26632             var styles = node.getAttribute("style").split(";");
26633             var nstyle = [];
26634             Roo.each(styles, function(s) {
26635                 if (!s.match(/:/)) {
26636                     return;
26637                 }
26638                 var kv = s.split(":");
26639                 if (kv[0].match(/^(mso-|line|font|background|margin|padding|color)/)) {
26640                     return;
26641                 }
26642                 // what ever is left... we allow.
26643                 nstyle.push(s);
26644             });
26645             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
26646             if (!nstyle.length) {
26647                 node.removeAttribute('style');
26648             }
26649         }
26650         return true; // do children
26651         
26652         
26653         
26654     },
26655     
26656     styleToObject: function(node)
26657     {
26658         var styles = (node.getAttribute("style") || '').split(";");
26659         var ret = {};
26660         Roo.each(styles, function(s) {
26661             if (!s.match(/:/)) {
26662                 return;
26663             }
26664             var kv = s.split(":");
26665              
26666             // what ever is left... we allow.
26667             ret[kv[0].trim()] = kv[1];
26668         });
26669         return ret;
26670     },
26671     
26672     
26673     replaceAname : function (doc)
26674     {
26675         // replace all the a/name without..
26676         var aa = Array.from(doc.getElementsByTagName('a'));
26677         for (var i = 0; i  < aa.length; i++) {
26678             var a = aa[i];
26679             if (a.hasAttribute("name")) {
26680                 a.removeAttribute("name");
26681             }
26682             if (a.hasAttribute("href")) {
26683                 continue;
26684             }
26685             // reparent children.
26686             this.removeNodeKeepChildren(a);
26687             
26688         }
26689         
26690         
26691         
26692     },
26693
26694     
26695     
26696     replaceDocBullets : function(doc)
26697     {
26698         // this is a bit odd - but it appears some indents use ql-indent-1
26699          //Roo.log(doc.innerHTML);
26700         
26701         var listpara = Array.from(doc.getElementsByClassName('MsoListParagraphCxSpFirst'));
26702         for( var i = 0; i < listpara.length; i ++) {
26703             listpara[i].className = "MsoListParagraph";
26704         }
26705         
26706         listpara =  Array.from(doc.getElementsByClassName('MsoListParagraphCxSpMiddle'));
26707         for( var i = 0; i < listpara.length; i ++) {
26708             listpara[i].className = "MsoListParagraph";
26709         }
26710         listpara =  Array.from(doc.getElementsByClassName('MsoListParagraphCxSpLast'));
26711         for( var i = 0; i < listpara.length; i ++) {
26712             listpara[i].className = "MsoListParagraph";
26713         }
26714         listpara =  Array.from(doc.getElementsByClassName('ql-indent-1'));
26715         for( var i = 0; i < listpara.length; i ++) {
26716             listpara[i].className = "MsoListParagraph";
26717         }
26718         
26719         // this is a bit hacky - we had one word document where h2 had a miso-list attribute.
26720         var htwo =  Array.from(doc.getElementsByTagName('h2'));
26721         for( var i = 0; i < htwo.length; i ++) {
26722             if (htwo[i].hasAttribute('style') && htwo[i].getAttribute('style').match(/mso-list:/)) {
26723                 htwo[i].className = "MsoListParagraph";
26724             }
26725         }
26726         listpara =  Array.from(doc.getElementsByClassName('MsoNormal'));
26727         for( var i = 0; i < listpara.length; i ++) {
26728             if (listpara[i].hasAttribute('style') && listpara[i].getAttribute('style').match(/mso-list:/)) {
26729                 listpara[i].className = "MsoListParagraph";
26730             } else {
26731                 listpara[i].className = "MsoNormalx";
26732             }
26733         }
26734        
26735         listpara = doc.getElementsByClassName('MsoListParagraph');
26736         // Roo.log(doc.innerHTML);
26737         
26738         
26739         
26740         while(listpara.length) {
26741             
26742             this.replaceDocBullet(listpara.item(0));
26743         }
26744       
26745     },
26746     
26747      
26748     
26749     replaceDocBullet : function(p)
26750     {
26751         // gather all the siblings.
26752         var ns = p,
26753             parent = p.parentNode,
26754             doc = parent.ownerDocument,
26755             items = [];
26756             
26757         var listtype = 'ul';   
26758         while (ns) {
26759             if (ns.nodeType != 1) {
26760                 ns = ns.nextSibling;
26761                 continue;
26762             }
26763             if (!ns.className.match(/(MsoListParagraph|ql-indent-1)/i)) {
26764                 break;
26765             }
26766             var spans = ns.getElementsByTagName('span');
26767             if (ns.hasAttribute('style') && ns.getAttribute('style').match(/mso-list/)) {
26768                 items.push(ns);
26769                 ns = ns.nextSibling;
26770                 has_list = true;
26771                 if (spans.length && spans[0].hasAttribute('style')) {
26772                     var  style = this.styleToObject(spans[0]);
26773                     if (typeof(style['font-family']) != 'undefined' && !style['font-family'].match(/Symbol/)) {
26774                         listtype = 'ol';
26775                     }
26776                 }
26777                 
26778                 continue;
26779             }
26780             var spans = ns.getElementsByTagName('span');
26781             if (!spans.length) {
26782                 break;
26783             }
26784             var has_list  = false;
26785             for(var i = 0; i < spans.length; i++) {
26786                 if (spans[i].hasAttribute('style') && spans[i].getAttribute('style').match(/mso-list/)) {
26787                     has_list = true;
26788                     break;
26789                 }
26790             }
26791             if (!has_list) {
26792                 break;
26793             }
26794             items.push(ns);
26795             ns = ns.nextSibling;
26796             
26797             
26798         }
26799         if (!items.length) {
26800             ns.className = "";
26801             return;
26802         }
26803         
26804         var ul = parent.ownerDocument.createElement(listtype); // what about number lists...
26805         parent.insertBefore(ul, p);
26806         var lvl = 0;
26807         var stack = [ ul ];
26808         var last_li = false;
26809         
26810         var margin_to_depth = {};
26811         max_margins = -1;
26812         
26813         items.forEach(function(n, ipos) {
26814             //Roo.log("got innertHMLT=" + n.innerHTML);
26815             
26816             var spans = n.getElementsByTagName('span');
26817             if (!spans.length) {
26818                 //Roo.log("No spans found");
26819                  
26820                 parent.removeChild(n);
26821                 
26822                 
26823                 return; // skip it...
26824             }
26825            
26826                 
26827             var num = 1;
26828             var style = {};
26829             for(var i = 0; i < spans.length; i++) {
26830             
26831                 style = this.styleToObject(spans[i]);
26832                 if (typeof(style['mso-list']) == 'undefined') {
26833                     continue;
26834                 }
26835                 if (listtype == 'ol') {
26836                    num = spans[i].innerText.replace(/[^0-9]+]/g,'')  * 1;
26837                 }
26838                 spans[i].parentNode.removeChild(spans[i]); // remove the fake bullet.
26839                 break;
26840             }
26841             //Roo.log("NOW GOT innertHMLT=" + n.innerHTML);
26842             style = this.styleToObject(n); // mo-list is from the parent node.
26843             if (typeof(style['mso-list']) == 'undefined') {
26844                 //Roo.log("parent is missing level");
26845                   
26846                 parent.removeChild(n);
26847                  
26848                 return;
26849             }
26850             
26851             var margin = style['margin-left'];
26852             if (typeof(margin_to_depth[margin]) == 'undefined') {
26853                 max_margins++;
26854                 margin_to_depth[margin] = max_margins;
26855             }
26856             nlvl = margin_to_depth[margin] ;
26857              
26858             if (nlvl > lvl) {
26859                 //new indent
26860                 var nul = doc.createElement(listtype); // what about number lists...
26861                 if (!last_li) {
26862                     last_li = doc.createElement('li');
26863                     stack[lvl].appendChild(last_li);
26864                 }
26865                 last_li.appendChild(nul);
26866                 stack[nlvl] = nul;
26867                 
26868             }
26869             lvl = nlvl;
26870             
26871             // not starting at 1..
26872             if (!stack[nlvl].hasAttribute("start") && listtype == "ol") {
26873                 stack[nlvl].setAttribute("start", num);
26874             }
26875             
26876             var nli = stack[nlvl].appendChild(doc.createElement('li'));
26877             last_li = nli;
26878             nli.innerHTML = n.innerHTML;
26879             //Roo.log("innerHTML = " + n.innerHTML);
26880             parent.removeChild(n);
26881             
26882              
26883              
26884             
26885         },this);
26886         
26887         
26888         
26889         
26890     }
26891     
26892     
26893     
26894 });
26895 /**
26896  * @class Roo.htmleditor.FilterStyleToTag
26897  * part of the word stuff... - certain 'styles' should be converted to tags.
26898  * eg.
26899  *   font-weight: bold -> bold
26900  *   ?? super / subscrit etc..
26901  * 
26902  * @constructor
26903 * Run a new style to tag filter.
26904 * @param {Object} config Configuration options
26905  */
26906 Roo.htmleditor.FilterStyleToTag = function(cfg)
26907 {
26908     
26909     this.tags = {
26910         B  : [ 'fontWeight' , 'bold'],
26911         I :  [ 'fontStyle' , 'italic'],
26912         //pre :  [ 'font-style' , 'italic'],
26913         // h1.. h6 ?? font-size?
26914         SUP : [ 'verticalAlign' , 'super' ],
26915         SUB : [ 'verticalAlign' , 'sub' ]
26916         
26917         
26918     };
26919     
26920     Roo.apply(this, cfg);
26921      
26922     
26923     this.walk(cfg.node);
26924     
26925     
26926     
26927 }
26928
26929
26930 Roo.extend(Roo.htmleditor.FilterStyleToTag, Roo.htmleditor.Filter,
26931 {
26932     tag: true, // all tags
26933     
26934     tags : false,
26935     
26936     
26937     replaceTag : function(node)
26938     {
26939         
26940         
26941         if (node.getAttribute("style") === null) {
26942             return true;
26943         }
26944         var inject = [];
26945         for (var k in this.tags) {
26946             if (node.style[this.tags[k][0]] == this.tags[k][1]) {
26947                 inject.push(k);
26948                 node.style.removeProperty(this.tags[k][0]);
26949             }
26950         }
26951         if (!inject.length) {
26952             return true; 
26953         }
26954         var cn = Array.from(node.childNodes);
26955         var nn = node;
26956         Roo.each(inject, function(t) {
26957             var nc = node.ownerDocument.createElement(t);
26958             nn.appendChild(nc);
26959             nn = nc;
26960         });
26961         for(var i = 0;i < cn.length;cn++) {
26962             node.removeChild(cn[i]);
26963             nn.appendChild(cn[i]);
26964         }
26965         return true /// iterate thru
26966     }
26967     
26968 })/**
26969  * @class Roo.htmleditor.FilterLongBr
26970  * BR/BR/BR - keep a maximum of 2...
26971  * @constructor
26972  * Run a new Long BR Filter
26973  * @param {Object} config Configuration options
26974  */
26975
26976 Roo.htmleditor.FilterLongBr = function(cfg)
26977 {
26978     // no need to apply config.
26979     this.walk(cfg.node);
26980 }
26981
26982 Roo.extend(Roo.htmleditor.FilterLongBr, Roo.htmleditor.Filter,
26983 {
26984     
26985      
26986     tag : 'BR',
26987     
26988      
26989     replaceTag : function(node)
26990     {
26991         
26992         var ps = node.nextSibling;
26993         while (ps && ps.nodeType == 3 && ps.nodeValue.trim().length < 1) {
26994             ps = ps.nextSibling;
26995         }
26996         
26997         if (!ps &&  [ 'TD', 'TH', 'LI', 'H1', 'H2', 'H3', 'H4', 'H5', 'H6' ].indexOf(node.parentNode.tagName) > -1) { 
26998             node.parentNode.removeChild(node); // remove last BR inside one fo these tags
26999             return false;
27000         }
27001         
27002         if (!ps || ps.nodeType != 1) {
27003             return false;
27004         }
27005         
27006         if (!ps || ps.tagName != 'BR') {
27007            
27008             return false;
27009         }
27010         
27011         
27012         
27013         
27014         
27015         if (!node.previousSibling) {
27016             return false;
27017         }
27018         var ps = node.previousSibling;
27019         
27020         while (ps && ps.nodeType == 3 && ps.nodeValue.trim().length < 1) {
27021             ps = ps.previousSibling;
27022         }
27023         if (!ps || ps.nodeType != 1) {
27024             return false;
27025         }
27026         // if header or BR before.. then it's a candidate for removal.. - as we only want '2' of these..
27027         if (!ps || [ 'BR', 'H1', 'H2', 'H3', 'H4', 'H5', 'H6' ].indexOf(ps.tagName) < 0) {
27028             return false;
27029         }
27030         
27031         node.parentNode.removeChild(node); // remove me...
27032         
27033         return false; // no need to do children
27034
27035     }
27036     
27037 }); 
27038
27039 /**
27040  * @class Roo.htmleditor.FilterBlock
27041  * removes id / data-block and contenteditable that are associated with blocks
27042  * usage should be done on a cloned copy of the dom
27043  * @constructor
27044 * Run a new Attribute Filter { node : xxxx }}
27045 * @param {Object} config Configuration options
27046  */
27047 Roo.htmleditor.FilterBlock = function(cfg)
27048 {
27049     Roo.apply(this, cfg);
27050     var qa = cfg.node.querySelectorAll;
27051     this.removeAttributes('data-block');
27052     this.removeAttributes('contenteditable');
27053     this.removeAttributes('id');
27054     
27055 }
27056
27057 Roo.apply(Roo.htmleditor.FilterBlock.prototype,
27058 {
27059     node: true, // all tags
27060      
27061      
27062     removeAttributes : function(attr)
27063     {
27064         var ar = this.node.querySelectorAll('*[' + attr + ']');
27065         for (var i =0;i<ar.length;i++) {
27066             ar[i].removeAttribute(attr);
27067         }
27068     }
27069         
27070         
27071         
27072     
27073 });
27074 /***
27075  * This is based loosely on tinymce 
27076  * @class Roo.htmleditor.TidySerializer
27077  * https://github.com/thorn0/tinymce.html/blob/master/tinymce.html.js
27078  * @constructor
27079  * @method Serializer
27080  * @param {Object} settings Name/value settings object.
27081  */
27082
27083
27084 Roo.htmleditor.TidySerializer = function(settings)
27085 {
27086     Roo.apply(this, settings);
27087     
27088     this.writer = new Roo.htmleditor.TidyWriter(settings);
27089     
27090     
27091
27092 };
27093 Roo.htmleditor.TidySerializer.prototype = {
27094     
27095     /**
27096      * @param {boolean} inner do the inner of the node.
27097      */
27098     inner : false,
27099     
27100     writer : false,
27101     
27102     /**
27103     * Serializes the specified node into a string.
27104     *
27105     * @example
27106     * new tinymce.html.Serializer().serialize(new tinymce.html.DomParser().parse('<p>text</p>'));
27107     * @method serialize
27108     * @param {DomElement} node Node instance to serialize.
27109     * @return {String} String with HTML based on DOM tree.
27110     */
27111     serialize : function(node) {
27112         
27113         // = settings.validate;
27114         var writer = this.writer;
27115         var self  = this;
27116         this.handlers = {
27117             // #text
27118             3: function(node) {
27119                 
27120                 writer.text(node.nodeValue, node);
27121             },
27122             // #comment
27123             8: function(node) {
27124                 writer.comment(node.nodeValue);
27125             },
27126             // Processing instruction
27127             7: function(node) {
27128                 writer.pi(node.name, node.nodeValue);
27129             },
27130             // Doctype
27131             10: function(node) {
27132                 writer.doctype(node.nodeValue);
27133             },
27134             // CDATA
27135             4: function(node) {
27136                 writer.cdata(node.nodeValue);
27137             },
27138             // Document fragment
27139             11: function(node) {
27140                 node = node.firstChild;
27141                 if (!node) {
27142                     return;
27143                 }
27144                 while(node) {
27145                     self.walk(node);
27146                     node = node.nextSibling
27147                 }
27148             }
27149         };
27150         writer.reset();
27151         1 != node.nodeType || this.inner ? this.handlers[11](node) : this.walk(node);
27152         return writer.getContent();
27153     },
27154
27155     walk: function(node)
27156     {
27157         var attrName, attrValue, sortedAttrs, i, l, elementRule,
27158             handler = this.handlers[node.nodeType];
27159             
27160         if (handler) {
27161             handler(node);
27162             return;
27163         }
27164     
27165         var name = node.nodeName;
27166         var isEmpty = node.childNodes.length < 1;
27167       
27168         var writer = this.writer;
27169         var attrs = node.attributes;
27170         // Sort attributes
27171         
27172         writer.start(node.nodeName, attrs, isEmpty, node);
27173         if (isEmpty) {
27174             return;
27175         }
27176         node = node.firstChild;
27177         if (!node) {
27178             writer.end(name);
27179             return;
27180         }
27181         while (node) {
27182             this.walk(node);
27183             node = node.nextSibling;
27184         }
27185         writer.end(name);
27186         
27187     
27188     }
27189     // Serialize element and treat all non elements as fragments
27190    
27191 }; 
27192
27193 /***
27194  * This is based loosely on tinymce 
27195  * @class Roo.htmleditor.TidyWriter
27196  * https://github.com/thorn0/tinymce.html/blob/master/tinymce.html.js
27197  *
27198  * Known issues?
27199  * - not tested much with 'PRE' formated elements.
27200  * 
27201  *
27202  *
27203  */
27204
27205 Roo.htmleditor.TidyWriter = function(settings)
27206 {
27207     
27208     // indent, indentBefore, indentAfter, encode, htmlOutput, html = [];
27209     Roo.apply(this, settings);
27210     this.html = [];
27211     this.state = [];
27212      
27213     this.encode = Roo.htmleditor.TidyEntities.getEncodeFunc(settings.entity_encoding || 'raw', settings.entities);
27214   
27215 }
27216 Roo.htmleditor.TidyWriter.prototype = {
27217
27218  
27219     state : false,
27220     
27221     indent :  '  ',
27222     
27223     // part of state...
27224     indentstr : '',
27225     in_pre: false,
27226     in_inline : false,
27227     last_inline : false,
27228     encode : false,
27229      
27230     
27231             /**
27232     * Writes the a start element such as <p id="a">.
27233     *
27234     * @method start
27235     * @param {String} name Name of the element.
27236     * @param {Array} attrs Optional attribute array or undefined if it hasn't any.
27237     * @param {Boolean} empty Optional empty state if the tag should end like <br />.
27238     */
27239     start: function(name, attrs, empty, node)
27240     {
27241         var i, l, attr, value;
27242         
27243         // there are some situations where adding line break && indentation will not work. will not work.
27244         // <span / b / i ... formating?
27245         
27246         var in_inline = this.in_inline || Roo.htmleditor.TidyWriter.inline_elements.indexOf(name) > -1;
27247         var in_pre    = this.in_pre    || Roo.htmleditor.TidyWriter.whitespace_elements.indexOf(name) > -1;
27248         
27249         var is_short   = empty ? Roo.htmleditor.TidyWriter.shortend_elements.indexOf(name) > -1 : false;
27250         
27251         var add_lb = name == 'BR' ? false : in_inline;
27252         
27253         if (!add_lb && !this.in_pre && this.lastElementEndsWS()) {
27254             i_inline = false;
27255         }
27256
27257         var indentstr =  this.indentstr;
27258         
27259         // e_inline = elements that can be inline, but still allow \n before and after?
27260         // only 'BR' ??? any others?
27261         
27262         // ADD LINE BEFORE tage
27263         if (!this.in_pre) {
27264             if (in_inline) {
27265                 //code
27266                 if (name == 'BR') {
27267                     this.addLine();
27268                 } else if (this.lastElementEndsWS()) {
27269                     this.addLine();
27270                 } else{
27271                     // otherwise - no new line. (and dont indent.)
27272                     indentstr = '';
27273                 }
27274                 
27275             } else {
27276                 this.addLine();
27277             }
27278         } else {
27279             indentstr = '';
27280         }
27281         
27282         this.html.push(indentstr + '<', name.toLowerCase());
27283         
27284         if (attrs) {
27285             for (i = 0, l = attrs.length; i < l; i++) {
27286                 attr = attrs[i];
27287                 this.html.push(' ', attr.name, '="', this.encode(attr.value, true), '"');
27288             }
27289         }
27290      
27291         if (empty) {
27292             if (is_short) {
27293                 this.html[this.html.length] = '/>';
27294             } else {
27295                 this.html[this.html.length] = '></' + name.toLowerCase() + '>';
27296             }
27297             var e_inline = name == 'BR' ? false : this.in_inline;
27298             
27299             if (!e_inline && !this.in_pre) {
27300                 this.addLine();
27301             }
27302             return;
27303         
27304         }
27305         // not empty..
27306         this.html[this.html.length] = '>';
27307         
27308         // there is a special situation, where we need to turn on in_inline - if any of the imediate chidlren are one of these.
27309         /*
27310         if (!in_inline && !in_pre) {
27311             var cn = node.firstChild;
27312             while(cn) {
27313                 if (Roo.htmleditor.TidyWriter.inline_elements.indexOf(cn.nodeName) > -1) {
27314                     in_inline = true
27315                     break;
27316                 }
27317                 cn = cn.nextSibling;
27318             }
27319              
27320         }
27321         */
27322         
27323         
27324         this.pushState({
27325             indentstr : in_pre   ? '' : (this.indentstr + this.indent),
27326             in_pre : in_pre,
27327             in_inline :  in_inline
27328         });
27329         // add a line after if we are not in a
27330         
27331         if (!in_inline && !in_pre) {
27332             this.addLine();
27333         }
27334         
27335             
27336          
27337         
27338     },
27339     
27340     lastElementEndsWS : function()
27341     {
27342         var value = this.html.length > 0 ? this.html[this.html.length-1] : false;
27343         if (value === false) {
27344             return true;
27345         }
27346         return value.match(/\s+$/);
27347         
27348     },
27349     
27350     /**
27351      * Writes the a end element such as </p>.
27352      *
27353      * @method end
27354      * @param {String} name Name of the element.
27355      */
27356     end: function(name) {
27357         var value;
27358         this.popState();
27359         var indentstr = '';
27360         var in_inline = this.in_inline || Roo.htmleditor.TidyWriter.inline_elements.indexOf(name) > -1;
27361         
27362         if (!this.in_pre && !in_inline) {
27363             this.addLine();
27364             indentstr  = this.indentstr;
27365         }
27366         this.html.push(indentstr + '</', name.toLowerCase(), '>');
27367         this.last_inline = in_inline;
27368         
27369         // pop the indent state..
27370     },
27371     /**
27372      * Writes a text node.
27373      *
27374      * In pre - we should not mess with the contents.
27375      * 
27376      *
27377      * @method text
27378      * @param {String} text String to write out.
27379      * @param {Boolean} raw Optional raw state if true the contents wont get encoded.
27380      */
27381     text: function(in_text, node)
27382     {
27383         // if not in whitespace critical
27384         if (in_text.length < 1) {
27385             return;
27386         }
27387         var text = new XMLSerializer().serializeToString(document.createTextNode(in_text)); // escape it properly?
27388         
27389         if (this.in_pre) {
27390             this.html[this.html.length] =  text;
27391             return;   
27392         }
27393         
27394         if (this.in_inline) {
27395             text = text.replace(/\s+/g,' '); // all white space inc line breaks to a slingle' '
27396             if (text != ' ') {
27397                 text = text.replace(/\s+/,' ');  // all white space to single white space
27398                 
27399                     
27400                 // if next tag is '<BR>', then we can trim right..
27401                 if (node.nextSibling &&
27402                     node.nextSibling.nodeType == 1 &&
27403                     node.nextSibling.nodeName == 'BR' )
27404                 {
27405                     text = text.replace(/\s+$/g,'');
27406                 }
27407                 // if previous tag was a BR, we can also trim..
27408                 if (node.previousSibling &&
27409                     node.previousSibling.nodeType == 1 &&
27410                     node.previousSibling.nodeName == 'BR' )
27411                 {
27412                     text = this.indentstr +  text.replace(/^\s+/g,'');
27413                 }
27414                 if (text.match(/\n/)) {
27415                     text = text.replace(
27416                         /(?![^\n]{1,64}$)([^\n]{1,64})\s/g, '$1\n' + this.indentstr
27417                     );
27418                     // remoeve the last whitespace / line break.
27419                     text = text.replace(/\n\s+$/,'');
27420                 }
27421                 // repace long lines
27422                 
27423             }
27424              
27425             this.html[this.html.length] =  text;
27426             return;   
27427         }
27428         // see if previous element was a inline element.
27429         var indentstr = this.indentstr;
27430    
27431         text = text.replace(/\s+/g," "); // all whitespace into single white space.
27432         
27433         // should trim left?
27434         if (node.previousSibling &&
27435             node.previousSibling.nodeType == 1 &&
27436             Roo.htmleditor.TidyWriter.inline_elements.indexOf(node.previousSibling.nodeName) > -1)
27437         {
27438             indentstr = '';
27439             
27440         } else {
27441             this.addLine();
27442             text = text.replace(/^\s+/,''); // trim left
27443           
27444         }
27445         // should trim right?
27446         if (node.nextSibling &&
27447             node.nextSibling.nodeType == 1 &&
27448             Roo.htmleditor.TidyWriter.inline_elements.indexOf(node.nextSibling.nodeName) > -1)
27449         {
27450           // noop
27451             
27452         }  else {
27453             text = text.replace(/\s+$/,''); // trim right
27454         }
27455          
27456               
27457         
27458         
27459         
27460         if (text.length < 1) {
27461             return;
27462         }
27463         if (!text.match(/\n/)) {
27464             this.html.push(indentstr + text);
27465             return;
27466         }
27467         
27468         text = this.indentstr + text.replace(
27469             /(?![^\n]{1,64}$)([^\n]{1,64})\s/g, '$1\n' + this.indentstr
27470         );
27471         // remoeve the last whitespace / line break.
27472         text = text.replace(/\s+$/,''); 
27473         
27474         this.html.push(text);
27475         
27476         // split and indent..
27477         
27478         
27479     },
27480     /**
27481      * Writes a cdata node such as <![CDATA[data]]>.
27482      *
27483      * @method cdata
27484      * @param {String} text String to write out inside the cdata.
27485      */
27486     cdata: function(text) {
27487         this.html.push('<![CDATA[', text, ']]>');
27488     },
27489     /**
27490     * Writes a comment node such as <!-- Comment -->.
27491     *
27492     * @method cdata
27493     * @param {String} text String to write out inside the comment.
27494     */
27495    comment: function(text) {
27496        this.html.push('<!--', text, '-->');
27497    },
27498     /**
27499      * Writes a PI node such as <?xml attr="value" ?>.
27500      *
27501      * @method pi
27502      * @param {String} name Name of the pi.
27503      * @param {String} text String to write out inside the pi.
27504      */
27505     pi: function(name, text) {
27506         text ? this.html.push('<?', name, ' ', this.encode(text), '?>') : this.html.push('<?', name, '?>');
27507         this.indent != '' && this.html.push('\n');
27508     },
27509     /**
27510      * Writes a doctype node such as <!DOCTYPE data>.
27511      *
27512      * @method doctype
27513      * @param {String} text String to write out inside the doctype.
27514      */
27515     doctype: function(text) {
27516         this.html.push('<!DOCTYPE', text, '>', this.indent != '' ? '\n' : '');
27517     },
27518     /**
27519      * Resets the internal buffer if one wants to reuse the writer.
27520      *
27521      * @method reset
27522      */
27523     reset: function() {
27524         this.html.length = 0;
27525         this.state = [];
27526         this.pushState({
27527             indentstr : '',
27528             in_pre : false, 
27529             in_inline : false
27530         })
27531     },
27532     /**
27533      * Returns the contents that got serialized.
27534      *
27535      * @method getContent
27536      * @return {String} HTML contents that got written down.
27537      */
27538     getContent: function() {
27539         return this.html.join('').replace(/\n$/, '');
27540     },
27541     
27542     pushState : function(cfg)
27543     {
27544         this.state.push(cfg);
27545         Roo.apply(this, cfg);
27546     },
27547     
27548     popState : function()
27549     {
27550         if (this.state.length < 1) {
27551             return; // nothing to push
27552         }
27553         var cfg = {
27554             in_pre: false,
27555             indentstr : ''
27556         };
27557         this.state.pop();
27558         if (this.state.length > 0) {
27559             cfg = this.state[this.state.length-1]; 
27560         }
27561         Roo.apply(this, cfg);
27562     },
27563     
27564     addLine: function()
27565     {
27566         if (this.html.length < 1) {
27567             return;
27568         }
27569         
27570         
27571         var value = this.html[this.html.length - 1];
27572         if (value.length > 0 && '\n' !== value) {
27573             this.html.push('\n');
27574         }
27575     }
27576     
27577     
27578 //'pre script noscript style textarea video audio iframe object code'
27579 // shortended... 'area base basefont br col frame hr img input isindex link  meta param embed source wbr track');
27580 // inline 
27581 };
27582
27583 Roo.htmleditor.TidyWriter.inline_elements = [
27584         'SPAN','STRONG','B','EM','I','FONT','STRIKE','U','VAR',
27585         'CITE','DFN','CODE','MARK','Q','SUP','SUB','SAMP', 'A'
27586 ];
27587 Roo.htmleditor.TidyWriter.shortend_elements = [
27588     'AREA','BASE','BASEFONT','BR','COL','FRAME','HR','IMG','INPUT',
27589     'ISINDEX','LINK','','META','PARAM','EMBED','SOURCE','WBR','TRACK'
27590 ];
27591
27592 Roo.htmleditor.TidyWriter.whitespace_elements = [
27593     'PRE','SCRIPT','NOSCRIPT','STYLE','TEXTAREA','VIDEO','AUDIO','IFRAME','OBJECT','CODE'
27594 ];/***
27595  * This is based loosely on tinymce 
27596  * @class Roo.htmleditor.TidyEntities
27597  * @static
27598  * https://github.com/thorn0/tinymce.html/blob/master/tinymce.html.js
27599  *
27600  * Not 100% sure this is actually used or needed.
27601  */
27602
27603 Roo.htmleditor.TidyEntities = {
27604     
27605     /**
27606      * initialize data..
27607      */
27608     init : function (){
27609      
27610         this.namedEntities = this.buildEntitiesLookup(this.namedEntitiesData, 32);
27611        
27612     },
27613
27614
27615     buildEntitiesLookup: function(items, radix) {
27616         var i, chr, entity, lookup = {};
27617         if (!items) {
27618             return {};
27619         }
27620         items = typeof(items) == 'string' ? items.split(',') : items;
27621         radix = radix || 10;
27622         // Build entities lookup table
27623         for (i = 0; i < items.length; i += 2) {
27624             chr = String.fromCharCode(parseInt(items[i], radix));
27625             // Only add non base entities
27626             if (!this.baseEntities[chr]) {
27627                 entity = '&' + items[i + 1] + ';';
27628                 lookup[chr] = entity;
27629                 lookup[entity] = chr;
27630             }
27631         }
27632         return lookup;
27633         
27634     },
27635     
27636     asciiMap : {
27637             128: '€',
27638             130: '‚',
27639             131: 'ƒ',
27640             132: '„',
27641             133: '…',
27642             134: '†',
27643             135: '‡',
27644             136: 'ˆ',
27645             137: '‰',
27646             138: 'Š',
27647             139: '‹',
27648             140: 'Œ',
27649             142: 'Ž',
27650             145: '‘',
27651             146: '’',
27652             147: '“',
27653             148: '”',
27654             149: '•',
27655             150: '–',
27656             151: '—',
27657             152: '˜',
27658             153: '™',
27659             154: 'š',
27660             155: '›',
27661             156: 'œ',
27662             158: 'ž',
27663             159: 'Ÿ'
27664     },
27665     // Raw entities
27666     baseEntities : {
27667         '"': '&quot;',
27668         // Needs to be escaped since the YUI compressor would otherwise break the code
27669         '\'': '&#39;',
27670         '<': '&lt;',
27671         '>': '&gt;',
27672         '&': '&amp;',
27673         '`': '&#96;'
27674     },
27675     // Reverse lookup table for raw entities
27676     reverseEntities : {
27677         '&lt;': '<',
27678         '&gt;': '>',
27679         '&amp;': '&',
27680         '&quot;': '"',
27681         '&apos;': '\''
27682     },
27683     
27684     attrsCharsRegExp : /[&<>\"\u0060\u007E-\uD7FF\uE000-\uFFEF]|[\uD800-\uDBFF][\uDC00-\uDFFF]/g,
27685     textCharsRegExp : /[<>&\u007E-\uD7FF\uE000-\uFFEF]|[\uD800-\uDBFF][\uDC00-\uDFFF]/g,
27686     rawCharsRegExp : /[<>&\"\']/g,
27687     entityRegExp : /&#([a-z0-9]+);?|&([a-z0-9]+);/gi,
27688     namedEntities  : false,
27689     namedEntitiesData : [ 
27690         '50',
27691         'nbsp',
27692         '51',
27693         'iexcl',
27694         '52',
27695         'cent',
27696         '53',
27697         'pound',
27698         '54',
27699         'curren',
27700         '55',
27701         'yen',
27702         '56',
27703         'brvbar',
27704         '57',
27705         'sect',
27706         '58',
27707         'uml',
27708         '59',
27709         'copy',
27710         '5a',
27711         'ordf',
27712         '5b',
27713         'laquo',
27714         '5c',
27715         'not',
27716         '5d',
27717         'shy',
27718         '5e',
27719         'reg',
27720         '5f',
27721         'macr',
27722         '5g',
27723         'deg',
27724         '5h',
27725         'plusmn',
27726         '5i',
27727         'sup2',
27728         '5j',
27729         'sup3',
27730         '5k',
27731         'acute',
27732         '5l',
27733         'micro',
27734         '5m',
27735         'para',
27736         '5n',
27737         'middot',
27738         '5o',
27739         'cedil',
27740         '5p',
27741         'sup1',
27742         '5q',
27743         'ordm',
27744         '5r',
27745         'raquo',
27746         '5s',
27747         'frac14',
27748         '5t',
27749         'frac12',
27750         '5u',
27751         'frac34',
27752         '5v',
27753         'iquest',
27754         '60',
27755         'Agrave',
27756         '61',
27757         'Aacute',
27758         '62',
27759         'Acirc',
27760         '63',
27761         'Atilde',
27762         '64',
27763         'Auml',
27764         '65',
27765         'Aring',
27766         '66',
27767         'AElig',
27768         '67',
27769         'Ccedil',
27770         '68',
27771         'Egrave',
27772         '69',
27773         'Eacute',
27774         '6a',
27775         'Ecirc',
27776         '6b',
27777         'Euml',
27778         '6c',
27779         'Igrave',
27780         '6d',
27781         'Iacute',
27782         '6e',
27783         'Icirc',
27784         '6f',
27785         'Iuml',
27786         '6g',
27787         'ETH',
27788         '6h',
27789         'Ntilde',
27790         '6i',
27791         'Ograve',
27792         '6j',
27793         'Oacute',
27794         '6k',
27795         'Ocirc',
27796         '6l',
27797         'Otilde',
27798         '6m',
27799         'Ouml',
27800         '6n',
27801         'times',
27802         '6o',
27803         'Oslash',
27804         '6p',
27805         'Ugrave',
27806         '6q',
27807         'Uacute',
27808         '6r',
27809         'Ucirc',
27810         '6s',
27811         'Uuml',
27812         '6t',
27813         'Yacute',
27814         '6u',
27815         'THORN',
27816         '6v',
27817         'szlig',
27818         '70',
27819         'agrave',
27820         '71',
27821         'aacute',
27822         '72',
27823         'acirc',
27824         '73',
27825         'atilde',
27826         '74',
27827         'auml',
27828         '75',
27829         'aring',
27830         '76',
27831         'aelig',
27832         '77',
27833         'ccedil',
27834         '78',
27835         'egrave',
27836         '79',
27837         'eacute',
27838         '7a',
27839         'ecirc',
27840         '7b',
27841         'euml',
27842         '7c',
27843         'igrave',
27844         '7d',
27845         'iacute',
27846         '7e',
27847         'icirc',
27848         '7f',
27849         'iuml',
27850         '7g',
27851         'eth',
27852         '7h',
27853         'ntilde',
27854         '7i',
27855         'ograve',
27856         '7j',
27857         'oacute',
27858         '7k',
27859         'ocirc',
27860         '7l',
27861         'otilde',
27862         '7m',
27863         'ouml',
27864         '7n',
27865         'divide',
27866         '7o',
27867         'oslash',
27868         '7p',
27869         'ugrave',
27870         '7q',
27871         'uacute',
27872         '7r',
27873         'ucirc',
27874         '7s',
27875         'uuml',
27876         '7t',
27877         'yacute',
27878         '7u',
27879         'thorn',
27880         '7v',
27881         'yuml',
27882         'ci',
27883         'fnof',
27884         'sh',
27885         'Alpha',
27886         'si',
27887         'Beta',
27888         'sj',
27889         'Gamma',
27890         'sk',
27891         'Delta',
27892         'sl',
27893         'Epsilon',
27894         'sm',
27895         'Zeta',
27896         'sn',
27897         'Eta',
27898         'so',
27899         'Theta',
27900         'sp',
27901         'Iota',
27902         'sq',
27903         'Kappa',
27904         'sr',
27905         'Lambda',
27906         'ss',
27907         'Mu',
27908         'st',
27909         'Nu',
27910         'su',
27911         'Xi',
27912         'sv',
27913         'Omicron',
27914         't0',
27915         'Pi',
27916         't1',
27917         'Rho',
27918         't3',
27919         'Sigma',
27920         't4',
27921         'Tau',
27922         't5',
27923         'Upsilon',
27924         't6',
27925         'Phi',
27926         't7',
27927         'Chi',
27928         't8',
27929         'Psi',
27930         't9',
27931         'Omega',
27932         'th',
27933         'alpha',
27934         'ti',
27935         'beta',
27936         'tj',
27937         'gamma',
27938         'tk',
27939         'delta',
27940         'tl',
27941         'epsilon',
27942         'tm',
27943         'zeta',
27944         'tn',
27945         'eta',
27946         'to',
27947         'theta',
27948         'tp',
27949         'iota',
27950         'tq',
27951         'kappa',
27952         'tr',
27953         'lambda',
27954         'ts',
27955         'mu',
27956         'tt',
27957         'nu',
27958         'tu',
27959         'xi',
27960         'tv',
27961         'omicron',
27962         'u0',
27963         'pi',
27964         'u1',
27965         'rho',
27966         'u2',
27967         'sigmaf',
27968         'u3',
27969         'sigma',
27970         'u4',
27971         'tau',
27972         'u5',
27973         'upsilon',
27974         'u6',
27975         'phi',
27976         'u7',
27977         'chi',
27978         'u8',
27979         'psi',
27980         'u9',
27981         'omega',
27982         'uh',
27983         'thetasym',
27984         'ui',
27985         'upsih',
27986         'um',
27987         'piv',
27988         '812',
27989         'bull',
27990         '816',
27991         'hellip',
27992         '81i',
27993         'prime',
27994         '81j',
27995         'Prime',
27996         '81u',
27997         'oline',
27998         '824',
27999         'frasl',
28000         '88o',
28001         'weierp',
28002         '88h',
28003         'image',
28004         '88s',
28005         'real',
28006         '892',
28007         'trade',
28008         '89l',
28009         'alefsym',
28010         '8cg',
28011         'larr',
28012         '8ch',
28013         'uarr',
28014         '8ci',
28015         'rarr',
28016         '8cj',
28017         'darr',
28018         '8ck',
28019         'harr',
28020         '8dl',
28021         'crarr',
28022         '8eg',
28023         'lArr',
28024         '8eh',
28025         'uArr',
28026         '8ei',
28027         'rArr',
28028         '8ej',
28029         'dArr',
28030         '8ek',
28031         'hArr',
28032         '8g0',
28033         'forall',
28034         '8g2',
28035         'part',
28036         '8g3',
28037         'exist',
28038         '8g5',
28039         'empty',
28040         '8g7',
28041         'nabla',
28042         '8g8',
28043         'isin',
28044         '8g9',
28045         'notin',
28046         '8gb',
28047         'ni',
28048         '8gf',
28049         'prod',
28050         '8gh',
28051         'sum',
28052         '8gi',
28053         'minus',
28054         '8gn',
28055         'lowast',
28056         '8gq',
28057         'radic',
28058         '8gt',
28059         'prop',
28060         '8gu',
28061         'infin',
28062         '8h0',
28063         'ang',
28064         '8h7',
28065         'and',
28066         '8h8',
28067         'or',
28068         '8h9',
28069         'cap',
28070         '8ha',
28071         'cup',
28072         '8hb',
28073         'int',
28074         '8hk',
28075         'there4',
28076         '8hs',
28077         'sim',
28078         '8i5',
28079         'cong',
28080         '8i8',
28081         'asymp',
28082         '8j0',
28083         'ne',
28084         '8j1',
28085         'equiv',
28086         '8j4',
28087         'le',
28088         '8j5',
28089         'ge',
28090         '8k2',
28091         'sub',
28092         '8k3',
28093         'sup',
28094         '8k4',
28095         'nsub',
28096         '8k6',
28097         'sube',
28098         '8k7',
28099         'supe',
28100         '8kl',
28101         'oplus',
28102         '8kn',
28103         'otimes',
28104         '8l5',
28105         'perp',
28106         '8m5',
28107         'sdot',
28108         '8o8',
28109         'lceil',
28110         '8o9',
28111         'rceil',
28112         '8oa',
28113         'lfloor',
28114         '8ob',
28115         'rfloor',
28116         '8p9',
28117         'lang',
28118         '8pa',
28119         'rang',
28120         '9ea',
28121         'loz',
28122         '9j0',
28123         'spades',
28124         '9j3',
28125         'clubs',
28126         '9j5',
28127         'hearts',
28128         '9j6',
28129         'diams',
28130         'ai',
28131         'OElig',
28132         'aj',
28133         'oelig',
28134         'b0',
28135         'Scaron',
28136         'b1',
28137         'scaron',
28138         'bo',
28139         'Yuml',
28140         'm6',
28141         'circ',
28142         'ms',
28143         'tilde',
28144         '802',
28145         'ensp',
28146         '803',
28147         'emsp',
28148         '809',
28149         'thinsp',
28150         '80c',
28151         'zwnj',
28152         '80d',
28153         'zwj',
28154         '80e',
28155         'lrm',
28156         '80f',
28157         'rlm',
28158         '80j',
28159         'ndash',
28160         '80k',
28161         'mdash',
28162         '80o',
28163         'lsquo',
28164         '80p',
28165         'rsquo',
28166         '80q',
28167         'sbquo',
28168         '80s',
28169         'ldquo',
28170         '80t',
28171         'rdquo',
28172         '80u',
28173         'bdquo',
28174         '810',
28175         'dagger',
28176         '811',
28177         'Dagger',
28178         '81g',
28179         'permil',
28180         '81p',
28181         'lsaquo',
28182         '81q',
28183         'rsaquo',
28184         '85c',
28185         'euro'
28186     ],
28187
28188          
28189     /**
28190      * Encodes the specified string using raw entities. This means only the required XML base entities will be encoded.
28191      *
28192      * @method encodeRaw
28193      * @param {String} text Text to encode.
28194      * @param {Boolean} attr Optional flag to specify if the text is attribute contents.
28195      * @return {String} Entity encoded text.
28196      */
28197     encodeRaw: function(text, attr)
28198     {
28199         var t = this;
28200         return text.replace(attr ? this.attrsCharsRegExp : this.textCharsRegExp, function(chr) {
28201             return t.baseEntities[chr] || chr;
28202         });
28203     },
28204     /**
28205      * Encoded the specified text with both the attributes and text entities. This function will produce larger text contents
28206      * since it doesn't know if the context is within a attribute or text node. This was added for compatibility
28207      * and is exposed as the DOMUtils.encode function.
28208      *
28209      * @method encodeAllRaw
28210      * @param {String} text Text to encode.
28211      * @return {String} Entity encoded text.
28212      */
28213     encodeAllRaw: function(text) {
28214         var t = this;
28215         return ('' + text).replace(this.rawCharsRegExp, function(chr) {
28216             return t.baseEntities[chr] || chr;
28217         });
28218     },
28219     /**
28220      * Encodes the specified string using numeric entities. The core entities will be
28221      * encoded as named ones but all non lower ascii characters will be encoded into numeric entities.
28222      *
28223      * @method encodeNumeric
28224      * @param {String} text Text to encode.
28225      * @param {Boolean} attr Optional flag to specify if the text is attribute contents.
28226      * @return {String} Entity encoded text.
28227      */
28228     encodeNumeric: function(text, attr) {
28229         var t = this;
28230         return text.replace(attr ? this.attrsCharsRegExp : this.textCharsRegExp, function(chr) {
28231             // Multi byte sequence convert it to a single entity
28232             if (chr.length > 1) {
28233                 return '&#' + (1024 * (chr.charCodeAt(0) - 55296) + (chr.charCodeAt(1) - 56320) + 65536) + ';';
28234             }
28235             return t.baseEntities[chr] || '&#' + chr.charCodeAt(0) + ';';
28236         });
28237     },
28238     /**
28239      * Encodes the specified string using named entities. The core entities will be encoded
28240      * as named ones but all non lower ascii characters will be encoded into named entities.
28241      *
28242      * @method encodeNamed
28243      * @param {String} text Text to encode.
28244      * @param {Boolean} attr Optional flag to specify if the text is attribute contents.
28245      * @param {Object} entities Optional parameter with entities to use.
28246      * @return {String} Entity encoded text.
28247      */
28248     encodeNamed: function(text, attr, entities) {
28249         var t = this;
28250         entities = entities || this.namedEntities;
28251         return text.replace(attr ? this.attrsCharsRegExp : this.textCharsRegExp, function(chr) {
28252             return t.baseEntities[chr] || entities[chr] || chr;
28253         });
28254     },
28255     /**
28256      * Returns an encode function based on the name(s) and it's optional entities.
28257      *
28258      * @method getEncodeFunc
28259      * @param {String} name Comma separated list of encoders for example named,numeric.
28260      * @param {String} entities Optional parameter with entities to use instead of the built in set.
28261      * @return {function} Encode function to be used.
28262      */
28263     getEncodeFunc: function(name, entities) {
28264         entities = this.buildEntitiesLookup(entities) || this.namedEntities;
28265         var t = this;
28266         function encodeNamedAndNumeric(text, attr) {
28267             return text.replace(attr ? t.attrsCharsRegExp : t.textCharsRegExp, function(chr) {
28268                 return t.baseEntities[chr] || entities[chr] || '&#' + chr.charCodeAt(0) + ';' || chr;
28269             });
28270         }
28271
28272         function encodeCustomNamed(text, attr) {
28273             return t.encodeNamed(text, attr, entities);
28274         }
28275         // Replace + with , to be compatible with previous TinyMCE versions
28276         name = this.makeMap(name.replace(/\+/g, ','));
28277         // Named and numeric encoder
28278         if (name.named && name.numeric) {
28279             return this.encodeNamedAndNumeric;
28280         }
28281         // Named encoder
28282         if (name.named) {
28283             // Custom names
28284             if (entities) {
28285                 return encodeCustomNamed;
28286             }
28287             return this.encodeNamed;
28288         }
28289         // Numeric
28290         if (name.numeric) {
28291             return this.encodeNumeric;
28292         }
28293         // Raw encoder
28294         return this.encodeRaw;
28295     },
28296     /**
28297      * Decodes the specified string, this will replace entities with raw UTF characters.
28298      *
28299      * @method decode
28300      * @param {String} text Text to entity decode.
28301      * @return {String} Entity decoded string.
28302      */
28303     decode: function(text)
28304     {
28305         var  t = this;
28306         return text.replace(this.entityRegExp, function(all, numeric) {
28307             if (numeric) {
28308                 numeric = 'x' === numeric.charAt(0).toLowerCase() ? parseInt(numeric.substr(1), 16) : parseInt(numeric, 10);
28309                 // Support upper UTF
28310                 if (numeric > 65535) {
28311                     numeric -= 65536;
28312                     return String.fromCharCode(55296 + (numeric >> 10), 56320 + (1023 & numeric));
28313                 }
28314                 return t.asciiMap[numeric] || String.fromCharCode(numeric);
28315             }
28316             return t.reverseEntities[all] || t.namedEntities[all] || t.nativeDecode(all);
28317         });
28318     },
28319     nativeDecode : function (text) {
28320         return text;
28321     },
28322     makeMap : function (items, delim, map) {
28323                 var i;
28324                 items = items || [];
28325                 delim = delim || ',';
28326                 if (typeof items == "string") {
28327                         items = items.split(delim);
28328                 }
28329                 map = map || {};
28330                 i = items.length;
28331                 while (i--) {
28332                         map[items[i]] = {};
28333                 }
28334                 return map;
28335         }
28336 };
28337     
28338     
28339     
28340 Roo.htmleditor.TidyEntities.init();
28341 /**
28342  * @class Roo.htmleditor.KeyEnter
28343  * Handle Enter press..
28344  * @cfg {Roo.HtmlEditorCore} core the editor.
28345  * @constructor
28346  * Create a new Filter.
28347  * @param {Object} config Configuration options
28348  */
28349
28350
28351
28352
28353
28354 Roo.htmleditor.KeyEnter = function(cfg) {
28355     Roo.apply(this, cfg);
28356     // this does not actually call walk as it's really just a abstract class
28357  
28358     Roo.get(this.core.doc.body).on('keypress', this.keypress, this);
28359 }
28360
28361 //Roo.htmleditor.KeyEnter.i = 0;
28362
28363
28364 Roo.htmleditor.KeyEnter.prototype = {
28365     
28366     core : false,
28367     
28368     keypress : function(e)
28369     {
28370         if (e.charCode != 13 && e.charCode != 10) {
28371             Roo.log([e.charCode,e]);
28372             return true;
28373         }
28374         e.preventDefault();
28375         // https://stackoverflow.com/questions/18552336/prevent-contenteditable-adding-div-on-enter-chrome
28376         var doc = this.core.doc;
28377           //add a new line
28378        
28379     
28380         var sel = this.core.getSelection();
28381         var range = sel.getRangeAt(0);
28382         var n = range.commonAncestorContainer;
28383         var pc = range.closest([ 'ol', 'ul']);
28384         var pli = range.closest('li');
28385         if (!pc || e.ctrlKey) {
28386             // on it list, or ctrl pressed.
28387             if (!e.ctrlKey) {
28388                 sel.insertNode('br', 'after'); 
28389             } else {
28390                 // only do this if we have ctrl key..
28391                 var br = doc.createElement('br');
28392                 br.className = 'clear';
28393                 br.setAttribute('style', 'clear: both');
28394                 sel.insertNode(br, 'after'); 
28395             }
28396             
28397          
28398             this.core.undoManager.addEvent();
28399             this.core.fireEditorEvent(e);
28400             return false;
28401         }
28402         
28403         // deal with <li> insetion
28404         if (pli.innerText.trim() == '' &&
28405             pli.previousSibling &&
28406             pli.previousSibling.nodeName == 'LI' &&
28407             pli.previousSibling.innerText.trim() ==  '') {
28408             pli.parentNode.removeChild(pli.previousSibling);
28409             sel.cursorAfter(pc);
28410             this.core.undoManager.addEvent();
28411             this.core.fireEditorEvent(e);
28412             return false;
28413         }
28414     
28415         var li = doc.createElement('LI');
28416         li.innerHTML = '&nbsp;';
28417         if (!pli || !pli.firstSibling) {
28418             pc.appendChild(li);
28419         } else {
28420             pli.parentNode.insertBefore(li, pli.firstSibling);
28421         }
28422         sel.cursorText (li.firstChild);
28423       
28424         this.core.undoManager.addEvent();
28425         this.core.fireEditorEvent(e);
28426
28427         return false;
28428         
28429     
28430         
28431         
28432          
28433     }
28434 };
28435      
28436 /**
28437  * @class Roo.htmleditor.Block
28438  * Base class for html editor blocks - do not use it directly .. extend it..
28439  * @cfg {DomElement} node The node to apply stuff to.
28440  * @cfg {String} friendly_name the name that appears in the context bar about this block
28441  * @cfg {Object} Context menu - see Roo.form.HtmlEditor.ToolbarContext
28442  
28443  * @constructor
28444  * Create a new Filter.
28445  * @param {Object} config Configuration options
28446  */
28447
28448 Roo.htmleditor.Block  = function(cfg)
28449 {
28450     // do nothing .. should not be called really.
28451 }
28452 /**
28453  * factory method to get the block from an element (using cache if necessary)
28454  * @static
28455  * @param {HtmlElement} the dom element
28456  */
28457 Roo.htmleditor.Block.factory = function(node)
28458 {
28459     var cc = Roo.htmleditor.Block.cache;
28460     var id = Roo.get(node).id;
28461     if (typeof(cc[id]) != 'undefined' && (!cc[id].node || cc[id].node.closest('body'))) {
28462         Roo.htmleditor.Block.cache[id].readElement(node);
28463         return Roo.htmleditor.Block.cache[id];
28464     }
28465     var db  = node.getAttribute('data-block');
28466     if (!db) {
28467         db = node.nodeName.toLowerCase().toUpperCaseFirst();
28468     }
28469     var cls = Roo.htmleditor['Block' + db];
28470     if (typeof(cls) == 'undefined') {
28471         //Roo.log(node.getAttribute('data-block'));
28472         Roo.log("OOps missing block : " + 'Block' + db);
28473         return false;
28474     }
28475     Roo.htmleditor.Block.cache[id] = new cls({ node: node });
28476     return Roo.htmleditor.Block.cache[id];  /// should trigger update element
28477 };
28478
28479 /**
28480  * initalize all Elements from content that are 'blockable'
28481  * @static
28482  * @param the body element
28483  */
28484 Roo.htmleditor.Block.initAll = function(body, type)
28485 {
28486     if (typeof(type) == 'undefined') {
28487         var ia = Roo.htmleditor.Block.initAll;
28488         ia(body,'table');
28489         ia(body,'td');
28490         ia(body,'figure');
28491         return;
28492     }
28493     Roo.each(Roo.get(body).query(type), function(e) {
28494         Roo.htmleditor.Block.factory(e);    
28495     },this);
28496 };
28497 // question goes here... do we need to clear out this cache sometimes?
28498 // or show we make it relivant to the htmleditor.
28499 Roo.htmleditor.Block.cache = {};
28500
28501 Roo.htmleditor.Block.prototype = {
28502     
28503     node : false,
28504     
28505      // used by context menu
28506     friendly_name : 'Based Block',
28507     
28508     // text for button to delete this element
28509     deleteTitle : false,
28510     
28511     context : false,
28512     /**
28513      * Update a node with values from this object
28514      * @param {DomElement} node
28515      */
28516     updateElement : function(node)
28517     {
28518         Roo.DomHelper.update(node === undefined ? this.node : node, this.toObject());
28519     },
28520      /**
28521      * convert to plain HTML for calling insertAtCursor..
28522      */
28523     toHTML : function()
28524     {
28525         return Roo.DomHelper.markup(this.toObject());
28526     },
28527     /**
28528      * used by readEleemnt to extract data from a node
28529      * may need improving as it's pretty basic
28530      
28531      * @param {DomElement} node
28532      * @param {String} tag - tag to find, eg. IMG ?? might be better to use DomQuery ?
28533      * @param {String} attribute (use html - for contents, style for using next param as style, or false to return the node)
28534      * @param {String} style the style property - eg. text-align
28535      */
28536     getVal : function(node, tag, attr, style)
28537     {
28538         var n = node;
28539         if (tag !== true && n.tagName != tag.toUpperCase()) {
28540             // in theory we could do figure[3] << 3rd figure? or some more complex search..?
28541             // but kiss for now.
28542             n = node.getElementsByTagName(tag).item(0);
28543         }
28544         if (!n) {
28545             return '';
28546         }
28547         if (attr === false) {
28548             return n;
28549         }
28550         if (attr == 'html') {
28551             return n.innerHTML;
28552         }
28553         if (attr == 'style') {
28554             return n.style[style]; 
28555         }
28556         
28557         return n.hasAttribute(attr) ? n.getAttribute(attr) : '';
28558             
28559     },
28560     /**
28561      * create a DomHelper friendly object - for use with 
28562      * Roo.DomHelper.markup / overwrite / etc..
28563      * (override this)
28564      */
28565     toObject : function()
28566     {
28567         return {};
28568     },
28569       /**
28570      * Read a node that has a 'data-block' property - and extract the values from it.
28571      * @param {DomElement} node - the node
28572      */
28573     readElement : function(node)
28574     {
28575         
28576     } 
28577     
28578     
28579 };
28580
28581  
28582
28583 /**
28584  * @class Roo.htmleditor.BlockFigure
28585  * Block that has an image and a figcaption
28586  * @cfg {String} image_src the url for the image
28587  * @cfg {String} align (left|right) alignment for the block default left
28588  * @cfg {String} caption the text to appear below  (and in the alt tag)
28589  * @cfg {String} caption_display (block|none) display or not the caption
28590  * @cfg {String|number} image_width the width of the image number or %?
28591  * @cfg {String|number} image_height the height of the image number or %?
28592  * 
28593  * @constructor
28594  * Create a new Filter.
28595  * @param {Object} config Configuration options
28596  */
28597
28598 Roo.htmleditor.BlockFigure = function(cfg)
28599 {
28600     if (cfg.node) {
28601         this.readElement(cfg.node);
28602         this.updateElement(cfg.node);
28603     }
28604     Roo.apply(this, cfg);
28605 }
28606 Roo.extend(Roo.htmleditor.BlockFigure, Roo.htmleditor.Block, {
28607  
28608     
28609     // setable values.
28610     image_src: '',
28611     align: 'center',
28612     caption : '',
28613     caption_display : 'block',
28614     width : '100%',
28615     cls : '',
28616     href: '',
28617     video_url : '',
28618     
28619     // margin: '2%', not used
28620     
28621     text_align: 'left', //   (left|right) alignment for the text caption default left. - not used at present
28622
28623     
28624     // used by context menu
28625     friendly_name : 'Image with caption',
28626     deleteTitle : "Delete Image and Caption",
28627     
28628     contextMenu : function(toolbar)
28629     {
28630         
28631         var block = function() {
28632             return Roo.htmleditor.Block.factory(toolbar.tb.selectedNode);
28633         };
28634         
28635         
28636         var rooui =  typeof(Roo.bootstrap) == 'undefined' ? Roo : Roo.bootstrap;
28637         
28638         var syncValue = toolbar.editorcore.syncValue;
28639         
28640         var fields = {};
28641         
28642         return [
28643              {
28644                 xtype : 'TextItem',
28645                 text : "Source: ",
28646                 xns : rooui.Toolbar  //Boostrap?
28647             },
28648             {
28649                 xtype : 'Button',
28650                 text: 'Change Image URL',
28651                  
28652                 listeners : {
28653                     click: function (btn, state)
28654                     {
28655                         var b = block();
28656                         
28657                         Roo.MessageBox.show({
28658                             title : "Image Source URL",
28659                             msg : "Enter the url for the image",
28660                             buttons: Roo.MessageBox.OKCANCEL,
28661                             fn: function(btn, val){
28662                                 if (btn != 'ok') {
28663                                     return;
28664                                 }
28665                                 b.image_src = val;
28666                                 b.updateElement();
28667                                 syncValue();
28668                                 toolbar.editorcore.onEditorEvent();
28669                             },
28670                             minWidth:250,
28671                             prompt:true,
28672                             //multiline: multiline,
28673                             modal : true,
28674                             value : b.image_src
28675                         });
28676                     }
28677                 },
28678                 xns : rooui.Toolbar
28679             },
28680          
28681             {
28682                 xtype : 'Button',
28683                 text: 'Change Link URL',
28684                  
28685                 listeners : {
28686                     click: function (btn, state)
28687                     {
28688                         var b = block();
28689                         
28690                         Roo.MessageBox.show({
28691                             title : "Link URL",
28692                             msg : "Enter the url for the link - leave blank to have no link",
28693                             buttons: Roo.MessageBox.OKCANCEL,
28694                             fn: function(btn, val){
28695                                 if (btn != 'ok') {
28696                                     return;
28697                                 }
28698                                 b.href = val;
28699                                 b.updateElement();
28700                                 syncValue();
28701                                 toolbar.editorcore.onEditorEvent();
28702                             },
28703                             minWidth:250,
28704                             prompt:true,
28705                             //multiline: multiline,
28706                             modal : true,
28707                             value : b.href
28708                         });
28709                     }
28710                 },
28711                 xns : rooui.Toolbar
28712             },
28713             {
28714                 xtype : 'Button',
28715                 text: 'Show Video URL',
28716                  
28717                 listeners : {
28718                     click: function (btn, state)
28719                     {
28720                         Roo.MessageBox.alert("Video URL",
28721                             block().video_url == '' ? 'This image is not linked ot a video' :
28722                                 'The image is linked to: <a target="_new" href="' + block().video_url + '">' + block().video_url + '</a>');
28723                     }
28724                 },
28725                 xns : rooui.Toolbar
28726             },
28727             
28728             
28729             {
28730                 xtype : 'TextItem',
28731                 text : "Width: ",
28732                 xns : rooui.Toolbar  //Boostrap?
28733             },
28734             {
28735                 xtype : 'ComboBox',
28736                 allowBlank : false,
28737                 displayField : 'val',
28738                 editable : true,
28739                 listWidth : 100,
28740                 triggerAction : 'all',
28741                 typeAhead : true,
28742                 valueField : 'val',
28743                 width : 70,
28744                 name : 'width',
28745                 listeners : {
28746                     select : function (combo, r, index)
28747                     {
28748                         toolbar.editorcore.selectNode(toolbar.tb.selectedNode);
28749                         var b = block();
28750                         b.width = r.get('val');
28751                         b.updateElement();
28752                         syncValue();
28753                         toolbar.editorcore.onEditorEvent();
28754                     }
28755                 },
28756                 xns : rooui.form,
28757                 store : {
28758                     xtype : 'SimpleStore',
28759                     data : [
28760                         ['100%'],
28761                         ['80%'],
28762                         ['50%'],
28763                         ['20%'],
28764                         ['10%']
28765                     ],
28766                     fields : [ 'val'],
28767                     xns : Roo.data
28768                 }
28769             },
28770             {
28771                 xtype : 'TextItem',
28772                 text : "Align: ",
28773                 xns : rooui.Toolbar  //Boostrap?
28774             },
28775             {
28776                 xtype : 'ComboBox',
28777                 allowBlank : false,
28778                 displayField : 'val',
28779                 editable : true,
28780                 listWidth : 100,
28781                 triggerAction : 'all',
28782                 typeAhead : true,
28783                 valueField : 'val',
28784                 width : 70,
28785                 name : 'align',
28786                 listeners : {
28787                     select : function (combo, r, index)
28788                     {
28789                         toolbar.editorcore.selectNode(toolbar.tb.selectedNode);
28790                         var b = block();
28791                         b.align = r.get('val');
28792                         b.updateElement();
28793                         syncValue();
28794                         toolbar.editorcore.onEditorEvent();
28795                     }
28796                 },
28797                 xns : rooui.form,
28798                 store : {
28799                     xtype : 'SimpleStore',
28800                     data : [
28801                         ['left'],
28802                         ['right'],
28803                         ['center']
28804                     ],
28805                     fields : [ 'val'],
28806                     xns : Roo.data
28807                 }
28808             },
28809             
28810             
28811             {
28812                 xtype : 'Button',
28813                 text: 'Hide Caption',
28814                 name : 'caption_display',
28815                 pressed : false,
28816                 enableToggle : true,
28817                 setValue : function(v) {
28818                     // this trigger toggle.
28819                      
28820                     this.setText(v ? "Hide Caption" : "Show Caption");
28821                     this.setPressed(v != 'block');
28822                 },
28823                 listeners : {
28824                     toggle: function (btn, state)
28825                     {
28826                         var b  = block();
28827                         b.caption_display = b.caption_display == 'block' ? 'none' : 'block';
28828                         this.setText(b.caption_display == 'block' ? "Hide Caption" : "Show Caption");
28829                         b.updateElement();
28830                         syncValue();
28831                         toolbar.editorcore.selectNode(toolbar.tb.selectedNode);
28832                         toolbar.editorcore.onEditorEvent();
28833                     }
28834                 },
28835                 xns : rooui.Toolbar
28836             }
28837         ];
28838         
28839     },
28840     /**
28841      * create a DomHelper friendly object - for use with
28842      * Roo.DomHelper.markup / overwrite / etc..
28843      */
28844     toObject : function()
28845     {
28846         var d = document.createElement('div');
28847         d.innerHTML = this.caption;
28848         
28849         var m = this.width != '100%' && this.align == 'center' ? '0 auto' : 0; 
28850         
28851         var iw = this.align == 'center' ? this.width : '100%';
28852         var img =   {
28853             tag : 'img',
28854             contenteditable : 'false',
28855             src : this.image_src,
28856             alt : d.innerText.replace(/\n/g, " ").replace(/\s+/g, ' ').trim(), // removeHTML and reduce spaces..
28857             style: {
28858                 width : iw,
28859                 maxWidth : iw + ' !important', // this is not getting rendered?
28860                 margin : m  
28861                 
28862             }
28863         };
28864         /*
28865         '<div class="{0}" width="420" height="315" src="{1}" frameborder="0" allowfullscreen>' +
28866                     '<a href="{2}">' + 
28867                         '<img class="{0}-thumbnail" src="{3}/Images/{4}/{5}#image-{4}" />' + 
28868                     '</a>' + 
28869                 '</div>',
28870         */
28871                 
28872         if (this.href.length > 0) {
28873             img = {
28874                 tag : 'a',
28875                 href: this.href,
28876                 contenteditable : 'true',
28877                 cn : [
28878                     img
28879                 ]
28880             };
28881         }
28882         
28883         
28884         if (this.video_url.length > 0) {
28885             img = {
28886                 tag : 'div',
28887                 cls : this.cls,
28888                 frameborder : 0,
28889                 allowfullscreen : true,
28890                 width : 420,  // these are for video tricks - that we replace the outer
28891                 height : 315,
28892                 src : this.video_url,
28893                 cn : [
28894                     img
28895                 ]
28896             };
28897         }
28898         // we remove caption totally if its hidden... - will delete data.. but otherwise we end up with fake caption
28899         var captionhtml = this.caption_display == 'none' ? '' : (this.caption.length ? this.caption : "Caption");
28900         
28901   
28902         var ret =   {
28903             tag: 'figure',
28904             'data-block' : 'Figure',
28905             'data-width' : this.width, 
28906             contenteditable : 'false',
28907             
28908             style : {
28909                 display: 'block',
28910                 float :  this.align ,
28911                 maxWidth :  this.align == 'center' ? '100% !important' : (this.width + ' !important'),
28912                 width : this.align == 'center' ? '100%' : this.width,
28913                 margin:  '0px',
28914                 padding: this.align == 'center' ? '0' : '0 10px' ,
28915                 textAlign : this.align   // seems to work for email..
28916                 
28917             },
28918            
28919             
28920             align : this.align,
28921             cn : [
28922                 img,
28923               
28924                 {
28925                     tag: 'figcaption',
28926                     'data-display' : this.caption_display,
28927                     style : {
28928                         textAlign : 'left',
28929                         fontSize : '16px',
28930                         lineHeight : '24px',
28931                         display : this.caption_display,
28932                         maxWidth : (this.align == 'center' ?  this.width : '100%' ) + ' !important',
28933                         margin: m,
28934                         width: this.align == 'center' ?  this.width : '100%' 
28935                     
28936                          
28937                     },
28938                     cls : this.cls.length > 0 ? (this.cls  + '-thumbnail' ) : '',
28939                     cn : [
28940                         {
28941                             tag: 'div',
28942                             style  : {
28943                                 marginTop : '16px',
28944                                 textAlign : 'left'
28945                             },
28946                             align: 'left',
28947                             cn : [
28948                                 {
28949                                     // we can not rely on yahoo syndication to use CSS elements - so have to use  '<i>' to encase stuff.
28950                                     tag : 'i',
28951                                     contenteditable : true,
28952                                     html : captionhtml
28953                                 }
28954                                 
28955                             ]
28956                         }
28957                         
28958                     ]
28959                     
28960                 }
28961             ]
28962         };
28963         return ret;
28964          
28965     },
28966     
28967     readElement : function(node)
28968     {
28969         // this should not really come from the link...
28970         this.video_url = this.getVal(node, 'div', 'src');
28971         this.cls = this.getVal(node, 'div', 'class');
28972         this.href = this.getVal(node, 'a', 'href');
28973         
28974         
28975         this.image_src = this.getVal(node, 'img', 'src');
28976          
28977         this.align = this.getVal(node, 'figure', 'align');
28978         var figcaption = this.getVal(node, 'figcaption', false);
28979         if (figcaption !== '') {
28980             this.caption = this.getVal(figcaption, 'i', 'html');
28981         }
28982         
28983
28984         this.caption_display = this.getVal(node, 'figcaption', 'data-display');
28985         //this.text_align = this.getVal(node, 'figcaption', 'style','text-align');
28986         this.width = this.getVal(node, true, 'data-width');
28987         //this.margin = this.getVal(node, 'figure', 'style', 'margin');
28988         
28989     },
28990     removeNode : function()
28991     {
28992         return this.node;
28993     }
28994     
28995   
28996    
28997      
28998     
28999     
29000     
29001     
29002 })
29003
29004  
29005
29006 /**
29007  * @class Roo.htmleditor.BlockTable
29008  * Block that manages a table
29009  * 
29010  * @constructor
29011  * Create a new Filter.
29012  * @param {Object} config Configuration options
29013  */
29014
29015 Roo.htmleditor.BlockTable = function(cfg)
29016 {
29017     if (cfg.node) {
29018         this.readElement(cfg.node);
29019         this.updateElement(cfg.node);
29020     }
29021     Roo.apply(this, cfg);
29022     if (!cfg.node) {
29023         this.rows = [];
29024         for(var r = 0; r < this.no_row; r++) {
29025             this.rows[r] = [];
29026             for(var c = 0; c < this.no_col; c++) {
29027                 this.rows[r][c] = this.emptyCell();
29028             }
29029         }
29030     }
29031     
29032     
29033 }
29034 Roo.extend(Roo.htmleditor.BlockTable, Roo.htmleditor.Block, {
29035  
29036     rows : false,
29037     no_col : 1,
29038     no_row : 1,
29039     
29040     
29041     width: '100%',
29042     
29043     // used by context menu
29044     friendly_name : 'Table',
29045     deleteTitle : 'Delete Table',
29046     // context menu is drawn once..
29047     
29048     contextMenu : function(toolbar)
29049     {
29050         
29051         var block = function() {
29052             return Roo.htmleditor.Block.factory(toolbar.tb.selectedNode);
29053         };
29054         
29055         
29056         var rooui =  typeof(Roo.bootstrap) == 'undefined' ? Roo : Roo.bootstrap;
29057         
29058         var syncValue = toolbar.editorcore.syncValue;
29059         
29060         var fields = {};
29061         
29062         return [
29063             {
29064                 xtype : 'TextItem',
29065                 text : "Width: ",
29066                 xns : rooui.Toolbar  //Boostrap?
29067             },
29068             {
29069                 xtype : 'ComboBox',
29070                 allowBlank : false,
29071                 displayField : 'val',
29072                 editable : true,
29073                 listWidth : 100,
29074                 triggerAction : 'all',
29075                 typeAhead : true,
29076                 valueField : 'val',
29077                 width : 100,
29078                 name : 'width',
29079                 listeners : {
29080                     select : function (combo, r, index)
29081                     {
29082                         toolbar.editorcore.selectNode(toolbar.tb.selectedNode);
29083                         var b = block();
29084                         b.width = r.get('val');
29085                         b.updateElement();
29086                         syncValue();
29087                         toolbar.editorcore.onEditorEvent();
29088                     }
29089                 },
29090                 xns : rooui.form,
29091                 store : {
29092                     xtype : 'SimpleStore',
29093                     data : [
29094                         ['100%'],
29095                         ['auto']
29096                     ],
29097                     fields : [ 'val'],
29098                     xns : Roo.data
29099                 }
29100             },
29101             // -------- Cols
29102             
29103             {
29104                 xtype : 'TextItem',
29105                 text : "Columns: ",
29106                 xns : rooui.Toolbar  //Boostrap?
29107             },
29108          
29109             {
29110                 xtype : 'Button',
29111                 text: '-',
29112                 listeners : {
29113                     click : function (_self, e)
29114                     {
29115                         toolbar.editorcore.selectNode(toolbar.tb.selectedNode);
29116                         block().removeColumn();
29117                         syncValue();
29118                         toolbar.editorcore.onEditorEvent();
29119                     }
29120                 },
29121                 xns : rooui.Toolbar
29122             },
29123             {
29124                 xtype : 'Button',
29125                 text: '+',
29126                 listeners : {
29127                     click : function (_self, e)
29128                     {
29129                         toolbar.editorcore.selectNode(toolbar.tb.selectedNode);
29130                         block().addColumn();
29131                         syncValue();
29132                         toolbar.editorcore.onEditorEvent();
29133                     }
29134                 },
29135                 xns : rooui.Toolbar
29136             },
29137             // -------- ROWS
29138             {
29139                 xtype : 'TextItem',
29140                 text : "Rows: ",
29141                 xns : rooui.Toolbar  //Boostrap?
29142             },
29143          
29144             {
29145                 xtype : 'Button',
29146                 text: '-',
29147                 listeners : {
29148                     click : function (_self, e)
29149                     {
29150                         toolbar.editorcore.selectNode(toolbar.tb.selectedNode);
29151                         block().removeRow();
29152                         syncValue();
29153                         toolbar.editorcore.onEditorEvent();
29154                     }
29155                 },
29156                 xns : rooui.Toolbar
29157             },
29158             {
29159                 xtype : 'Button',
29160                 text: '+',
29161                 listeners : {
29162                     click : function (_self, e)
29163                     {
29164                         block().addRow();
29165                         syncValue();
29166                         toolbar.editorcore.onEditorEvent();
29167                     }
29168                 },
29169                 xns : rooui.Toolbar
29170             },
29171             // -------- ROWS
29172             {
29173                 xtype : 'Button',
29174                 text: 'Reset Column Widths',
29175                 listeners : {
29176                     
29177                     click : function (_self, e)
29178                     {
29179                         block().resetWidths();
29180                         syncValue();
29181                         toolbar.editorcore.onEditorEvent();
29182                     }
29183                 },
29184                 xns : rooui.Toolbar
29185             } 
29186             
29187             
29188             
29189         ];
29190         
29191     },
29192     
29193     
29194   /**
29195      * create a DomHelper friendly object - for use with
29196      * Roo.DomHelper.markup / overwrite / etc..
29197      * ?? should it be called with option to hide all editing features?
29198      */
29199     toObject : function()
29200     {
29201         
29202         var ret = {
29203             tag : 'table',
29204             contenteditable : 'false', // this stops cell selection from picking the table.
29205             'data-block' : 'Table',
29206             style : {
29207                 width:  this.width,
29208                 border : 'solid 1px #000', // ??? hard coded?
29209                 'border-collapse' : 'collapse' 
29210             },
29211             cn : [
29212                 { tag : 'tbody' , cn : [] }
29213             ]
29214         };
29215         
29216         // do we have a head = not really 
29217         var ncols = 0;
29218         Roo.each(this.rows, function( row ) {
29219             var tr = {
29220                 tag: 'tr',
29221                 style : {
29222                     margin: '6px',
29223                     border : 'solid 1px #000',
29224                     textAlign : 'left' 
29225                 },
29226                 cn : [ ]
29227             };
29228             
29229             ret.cn[0].cn.push(tr);
29230             // does the row have any properties? ?? height?
29231             var nc = 0;
29232             Roo.each(row, function( cell ) {
29233                 
29234                 var td = {
29235                     tag : 'td',
29236                     contenteditable :  'true',
29237                     'data-block' : 'Td',
29238                     html : cell.html,
29239                     style : cell.style
29240                 };
29241                 if (cell.colspan > 1) {
29242                     td.colspan = cell.colspan ;
29243                     nc += cell.colspan;
29244                 } else {
29245                     nc++;
29246                 }
29247                 if (cell.rowspan > 1) {
29248                     td.rowspan = cell.rowspan ;
29249                 }
29250                 
29251                 
29252                 // widths ?
29253                 tr.cn.push(td);
29254                     
29255                 
29256             }, this);
29257             ncols = Math.max(nc, ncols);
29258             
29259             
29260         }, this);
29261         // add the header row..
29262         
29263         ncols++;
29264          
29265         
29266         return ret;
29267          
29268     },
29269     
29270     readElement : function(node)
29271     {
29272         node  = node ? node : this.node ;
29273         this.width = this.getVal(node, true, 'style', 'width') || '100%';
29274         
29275         this.rows = [];
29276         this.no_row = 0;
29277         var trs = Array.from(node.rows);
29278         trs.forEach(function(tr) {
29279             var row =  [];
29280             this.rows.push(row);
29281             
29282             this.no_row++;
29283             var no_column = 0;
29284             Array.from(tr.cells).forEach(function(td) {
29285                 
29286                 var add = {
29287                     colspan : td.hasAttribute('colspan') ? td.getAttribute('colspan')*1 : 1,
29288                     rowspan : td.hasAttribute('rowspan') ? td.getAttribute('rowspan')*1 : 1,
29289                     style : td.hasAttribute('style') ? td.getAttribute('style') : '',
29290                     html : td.innerHTML
29291                 };
29292                 no_column += add.colspan;
29293                      
29294                 
29295                 row.push(add);
29296                 
29297                 
29298             },this);
29299             this.no_col = Math.max(this.no_col, no_column);
29300             
29301             
29302         },this);
29303         
29304         
29305     },
29306     normalizeRows: function()
29307     {
29308         var ret= [];
29309         var rid = -1;
29310         this.rows.forEach(function(row) {
29311             rid++;
29312             ret[rid] = [];
29313             row = this.normalizeRow(row);
29314             var cid = 0;
29315             row.forEach(function(c) {
29316                 while (typeof(ret[rid][cid]) != 'undefined') {
29317                     cid++;
29318                 }
29319                 if (typeof(ret[rid]) == 'undefined') {
29320                     ret[rid] = [];
29321                 }
29322                 ret[rid][cid] = c;
29323                 c.row = rid;
29324                 c.col = cid;
29325                 if (c.rowspan < 2) {
29326                     return;
29327                 }
29328                 
29329                 for(var i = 1 ;i < c.rowspan; i++) {
29330                     if (typeof(ret[rid+i]) == 'undefined') {
29331                         ret[rid+i] = [];
29332                     }
29333                     ret[rid+i][cid] = c;
29334                 }
29335             });
29336         }, this);
29337         return ret;
29338     
29339     },
29340     
29341     normalizeRow: function(row)
29342     {
29343         var ret= [];
29344         row.forEach(function(c) {
29345             if (c.colspan < 2) {
29346                 ret.push(c);
29347                 return;
29348             }
29349             for(var i =0 ;i < c.colspan; i++) {
29350                 ret.push(c);
29351             }
29352         });
29353         return ret;
29354     
29355     },
29356     
29357     deleteColumn : function(sel)
29358     {
29359         if (!sel || sel.type != 'col') {
29360             return;
29361         }
29362         if (this.no_col < 2) {
29363             return;
29364         }
29365         
29366         this.rows.forEach(function(row) {
29367             var cols = this.normalizeRow(row);
29368             var col = cols[sel.col];
29369             if (col.colspan > 1) {
29370                 col.colspan --;
29371             } else {
29372                 row.remove(col);
29373             }
29374             
29375         }, this);
29376         this.no_col--;
29377         
29378     },
29379     removeColumn : function()
29380     {
29381         this.deleteColumn({
29382             type: 'col',
29383             col : this.no_col-1
29384         });
29385         this.updateElement();
29386     },
29387     
29388      
29389     addColumn : function()
29390     {
29391         
29392         this.rows.forEach(function(row) {
29393             row.push(this.emptyCell());
29394            
29395         }, this);
29396         this.updateElement();
29397     },
29398     
29399     deleteRow : function(sel)
29400     {
29401         if (!sel || sel.type != 'row') {
29402             return;
29403         }
29404         
29405         if (this.no_row < 2) {
29406             return;
29407         }
29408         
29409         var rows = this.normalizeRows();
29410         
29411         
29412         rows[sel.row].forEach(function(col) {
29413             if (col.rowspan > 1) {
29414                 col.rowspan--;
29415             } else {
29416                 col.remove = 1; // flage it as removed.
29417             }
29418             
29419         }, this);
29420         var newrows = [];
29421         this.rows.forEach(function(row) {
29422             newrow = [];
29423             row.forEach(function(c) {
29424                 if (typeof(c.remove) == 'undefined') {
29425                     newrow.push(c);
29426                 }
29427                 
29428             });
29429             if (newrow.length > 0) {
29430                 newrows.push(row);
29431             }
29432         });
29433         this.rows =  newrows;
29434         
29435         
29436         
29437         this.no_row--;
29438         this.updateElement();
29439         
29440     },
29441     removeRow : function()
29442     {
29443         this.deleteRow({
29444             type: 'row',
29445             row : this.no_row-1
29446         });
29447         
29448     },
29449     
29450      
29451     addRow : function()
29452     {
29453         
29454         var row = [];
29455         for (var i = 0; i < this.no_col; i++ ) {
29456             
29457             row.push(this.emptyCell());
29458            
29459         }
29460         this.rows.push(row);
29461         this.updateElement();
29462         
29463     },
29464      
29465     // the default cell object... at present...
29466     emptyCell : function() {
29467         return (new Roo.htmleditor.BlockTd({})).toObject();
29468         
29469      
29470     },
29471     
29472     removeNode : function()
29473     {
29474         return this.node;
29475     },
29476     
29477     
29478     
29479     resetWidths : function()
29480     {
29481         Array.from(this.node.getElementsByTagName('td')).forEach(function(n) {
29482             var nn = Roo.htmleditor.Block.factory(n);
29483             nn.width = '';
29484             nn.updateElement(n);
29485         });
29486     }
29487     
29488     
29489     
29490     
29491 })
29492
29493 /**
29494  *
29495  * editing a TD?
29496  *
29497  * since selections really work on the table cell, then editing really should work from there
29498  *
29499  * The original plan was to support merging etc... - but that may not be needed yet..
29500  *
29501  * So this simple version will support:
29502  *   add/remove cols
29503  *   adjust the width +/-
29504  *   reset the width...
29505  *   
29506  *
29507  */
29508
29509
29510  
29511
29512 /**
29513  * @class Roo.htmleditor.BlockTable
29514  * Block that manages a table
29515  * 
29516  * @constructor
29517  * Create a new Filter.
29518  * @param {Object} config Configuration options
29519  */
29520
29521 Roo.htmleditor.BlockTd = function(cfg)
29522 {
29523     if (cfg.node) {
29524         this.readElement(cfg.node);
29525         this.updateElement(cfg.node);
29526     }
29527     Roo.apply(this, cfg);
29528      
29529     
29530     
29531 }
29532 Roo.extend(Roo.htmleditor.BlockTd, Roo.htmleditor.Block, {
29533  
29534     node : false,
29535     
29536     width: '',
29537     textAlign : 'left',
29538     valign : 'top',
29539     
29540     colspan : 1,
29541     rowspan : 1,
29542     
29543     
29544     // used by context menu
29545     friendly_name : 'Table Cell',
29546     deleteTitle : false, // use our customer delete
29547     
29548     // context menu is drawn once..
29549     
29550     contextMenu : function(toolbar)
29551     {
29552         
29553         var cell = function() {
29554             return Roo.htmleditor.Block.factory(toolbar.tb.selectedNode);
29555         };
29556         
29557         var table = function() {
29558             return Roo.htmleditor.Block.factory(toolbar.tb.selectedNode.closest('table'));
29559         };
29560         
29561         var lr = false;
29562         var saveSel = function()
29563         {
29564             lr = toolbar.editorcore.getSelection().getRangeAt(0);
29565         }
29566         var restoreSel = function()
29567         {
29568             if (lr) {
29569                 (function() {
29570                     toolbar.editorcore.focus();
29571                     var cr = toolbar.editorcore.getSelection();
29572                     cr.removeAllRanges();
29573                     cr.addRange(lr);
29574                     toolbar.editorcore.onEditorEvent();
29575                 }).defer(10, this);
29576                 
29577                 
29578             }
29579         }
29580         
29581         var rooui =  typeof(Roo.bootstrap) == 'undefined' ? Roo : Roo.bootstrap;
29582         
29583         var syncValue = toolbar.editorcore.syncValue;
29584         
29585         var fields = {};
29586         
29587         return [
29588             {
29589                 xtype : 'Button',
29590                 text : 'Edit Table',
29591                 listeners : {
29592                     click : function() {
29593                         var t = toolbar.tb.selectedNode.closest('table');
29594                         toolbar.editorcore.selectNode(t);
29595                         toolbar.editorcore.onEditorEvent();                        
29596                     }
29597                 }
29598                 
29599             },
29600               
29601            
29602              
29603             {
29604                 xtype : 'TextItem',
29605                 text : "Column Width: ",
29606                  xns : rooui.Toolbar 
29607                
29608             },
29609             {
29610                 xtype : 'Button',
29611                 text: '-',
29612                 listeners : {
29613                     click : function (_self, e)
29614                     {
29615                         toolbar.editorcore.selectNode(toolbar.tb.selectedNode);
29616                         cell().shrinkColumn();
29617                         syncValue();
29618                          toolbar.editorcore.onEditorEvent();
29619                     }
29620                 },
29621                 xns : rooui.Toolbar
29622             },
29623             {
29624                 xtype : 'Button',
29625                 text: '+',
29626                 listeners : {
29627                     click : function (_self, e)
29628                     {
29629                         toolbar.editorcore.selectNode(toolbar.tb.selectedNode);
29630                         cell().growColumn();
29631                         syncValue();
29632                         toolbar.editorcore.onEditorEvent();
29633                     }
29634                 },
29635                 xns : rooui.Toolbar
29636             },
29637             
29638             {
29639                 xtype : 'TextItem',
29640                 text : "Vertical Align: ",
29641                 xns : rooui.Toolbar  //Boostrap?
29642             },
29643             {
29644                 xtype : 'ComboBox',
29645                 allowBlank : false,
29646                 displayField : 'val',
29647                 editable : true,
29648                 listWidth : 100,
29649                 triggerAction : 'all',
29650                 typeAhead : true,
29651                 valueField : 'val',
29652                 width : 100,
29653                 name : 'valign',
29654                 listeners : {
29655                     select : function (combo, r, index)
29656                     {
29657                         toolbar.editorcore.selectNode(toolbar.tb.selectedNode);
29658                         var b = cell();
29659                         b.valign = r.get('val');
29660                         b.updateElement();
29661                         syncValue();
29662                         toolbar.editorcore.onEditorEvent();
29663                     }
29664                 },
29665                 xns : rooui.form,
29666                 store : {
29667                     xtype : 'SimpleStore',
29668                     data : [
29669                         ['top'],
29670                         ['middle'],
29671                         ['bottom'] // there are afew more... 
29672                     ],
29673                     fields : [ 'val'],
29674                     xns : Roo.data
29675                 }
29676             },
29677             
29678             {
29679                 xtype : 'TextItem',
29680                 text : "Merge Cells: ",
29681                  xns : rooui.Toolbar 
29682                
29683             },
29684             
29685             
29686             {
29687                 xtype : 'Button',
29688                 text: 'Right',
29689                 listeners : {
29690                     click : function (_self, e)
29691                     {
29692                         toolbar.editorcore.selectNode(toolbar.tb.selectedNode);
29693                         cell().mergeRight();
29694                         //block().growColumn();
29695                         syncValue();
29696                         toolbar.editorcore.onEditorEvent();
29697                     }
29698                 },
29699                 xns : rooui.Toolbar
29700             },
29701              
29702             {
29703                 xtype : 'Button',
29704                 text: 'Below',
29705                 listeners : {
29706                     click : function (_self, e)
29707                     {
29708                         toolbar.editorcore.selectNode(toolbar.tb.selectedNode);
29709                         cell().mergeBelow();
29710                         //block().growColumn();
29711                         syncValue();
29712                         toolbar.editorcore.onEditorEvent();
29713                     }
29714                 },
29715                 xns : rooui.Toolbar
29716             },
29717             {
29718                 xtype : 'TextItem',
29719                 text : "| ",
29720                  xns : rooui.Toolbar 
29721                
29722             },
29723             
29724             {
29725                 xtype : 'Button',
29726                 text: 'Split',
29727                 listeners : {
29728                     click : function (_self, e)
29729                     {
29730                         //toolbar.editorcore.selectNode(toolbar.tb.selectedNode);
29731                         cell().split();
29732                         syncValue();
29733                         toolbar.editorcore.selectNode(toolbar.tb.selectedNode);
29734                         toolbar.editorcore.onEditorEvent();
29735                                              
29736                     }
29737                 },
29738                 xns : rooui.Toolbar
29739             },
29740             {
29741                 xtype : 'Fill',
29742                 xns : rooui.Toolbar 
29743                
29744             },
29745         
29746           
29747             {
29748                 xtype : 'Button',
29749                 text: 'Delete',
29750                  
29751                 xns : rooui.Toolbar,
29752                 menu : {
29753                     xtype : 'Menu',
29754                     xns : rooui.menu,
29755                     items : [
29756                         {
29757                             xtype : 'Item',
29758                             html: 'Column',
29759                             listeners : {
29760                                 click : function (_self, e)
29761                                 {
29762                                     var t = table();
29763                                     
29764                                     cell().deleteColumn();
29765                                     syncValue();
29766                                     toolbar.editorcore.selectNode(t.node);
29767                                     toolbar.editorcore.onEditorEvent();   
29768                                 }
29769                             },
29770                             xns : rooui.menu
29771                         },
29772                         {
29773                             xtype : 'Item',
29774                             html: 'Row',
29775                             listeners : {
29776                                 click : function (_self, e)
29777                                 {
29778                                     var t = table();
29779                                     cell().deleteRow();
29780                                     syncValue();
29781                                     
29782                                     toolbar.editorcore.selectNode(t.node);
29783                                     toolbar.editorcore.onEditorEvent();   
29784                                                          
29785                                 }
29786                             },
29787                             xns : rooui.menu
29788                         },
29789                        {
29790                             xtype : 'Separator',
29791                             xns : rooui.menu
29792                         },
29793                         {
29794                             xtype : 'Item',
29795                             html: 'Table',
29796                             listeners : {
29797                                 click : function (_self, e)
29798                                 {
29799                                     var t = table();
29800                                     var nn = t.node.nextSibling || t.node.previousSibling;
29801                                     t.node.parentNode.removeChild(t.node);
29802                                     if (nn) { 
29803                                         toolbar.editorcore.selectNode(nn, true);
29804                                     }
29805                                     toolbar.editorcore.onEditorEvent();   
29806                                                          
29807                                 }
29808                             },
29809                             xns : rooui.menu
29810                         }
29811                     ]
29812                 }
29813             }
29814             
29815             // align... << fixme
29816             
29817         ];
29818         
29819     },
29820     
29821     
29822   /**
29823      * create a DomHelper friendly object - for use with
29824      * Roo.DomHelper.markup / overwrite / etc..
29825      * ?? should it be called with option to hide all editing features?
29826      */
29827  /**
29828      * create a DomHelper friendly object - for use with
29829      * Roo.DomHelper.markup / overwrite / etc..
29830      * ?? should it be called with option to hide all editing features?
29831      */
29832     toObject : function()
29833     {
29834         var ret = {
29835             tag : 'td',
29836             contenteditable : 'true', // this stops cell selection from picking the table.
29837             'data-block' : 'Td',
29838             valign : this.valign,
29839             style : {  
29840                 'text-align' :  this.textAlign,
29841                 border : 'solid 1px rgb(0, 0, 0)', // ??? hard coded?
29842                 'border-collapse' : 'collapse',
29843                 padding : '6px', // 8 for desktop / 4 for mobile
29844                 'vertical-align': this.valign
29845             },
29846             html : this.html
29847         };
29848         if (this.width != '') {
29849             ret.width = this.width;
29850             ret.style.width = this.width;
29851         }
29852         
29853         
29854         if (this.colspan > 1) {
29855             ret.colspan = this.colspan ;
29856         } 
29857         if (this.rowspan > 1) {
29858             ret.rowspan = this.rowspan ;
29859         }
29860         
29861            
29862         
29863         return ret;
29864          
29865     },
29866     
29867     readElement : function(node)
29868     {
29869         node  = node ? node : this.node ;
29870         this.width = node.style.width;
29871         this.colspan = Math.max(1,1*node.getAttribute('colspan'));
29872         this.rowspan = Math.max(1,1*node.getAttribute('rowspan'));
29873         this.html = node.innerHTML;
29874         if (node.style.textAlign != '') {
29875             this.textAlign = node.style.textAlign;
29876         }
29877         
29878         
29879     },
29880      
29881     // the default cell object... at present...
29882     emptyCell : function() {
29883         return {
29884             colspan :  1,
29885             rowspan :  1,
29886             textAlign : 'left',
29887             html : "&nbsp;" // is this going to be editable now?
29888         };
29889      
29890     },
29891     
29892     removeNode : function()
29893     {
29894         return this.node.closest('table');
29895          
29896     },
29897     
29898     cellData : false,
29899     
29900     colWidths : false,
29901     
29902     toTableArray  : function()
29903     {
29904         var ret = [];
29905         var tab = this.node.closest('tr').closest('table');
29906         Array.from(tab.rows).forEach(function(r, ri){
29907             ret[ri] = [];
29908         });
29909         var rn = 0;
29910         this.colWidths = [];
29911         var all_auto = true;
29912         Array.from(tab.rows).forEach(function(r, ri){
29913             
29914             var cn = 0;
29915             Array.from(r.cells).forEach(function(ce, ci){
29916                 var c =  {
29917                     cell : ce,
29918                     row : rn,
29919                     col: cn,
29920                     colspan : ce.colSpan,
29921                     rowspan : ce.rowSpan
29922                 };
29923                 if (ce.isEqualNode(this.node)) {
29924                     this.cellData = c;
29925                 }
29926                 // if we have been filled up by a row?
29927                 if (typeof(ret[rn][cn]) != 'undefined') {
29928                     while(typeof(ret[rn][cn]) != 'undefined') {
29929                         cn++;
29930                     }
29931                     c.col = cn;
29932                 }
29933                 
29934                 if (typeof(this.colWidths[cn]) == 'undefined' && c.colspan < 2) {
29935                     this.colWidths[cn] =   ce.style.width;
29936                     if (this.colWidths[cn] != '') {
29937                         all_auto = false;
29938                     }
29939                 }
29940                 
29941                 
29942                 if (c.colspan < 2 && c.rowspan < 2 ) {
29943                     ret[rn][cn] = c;
29944                     cn++;
29945                     return;
29946                 }
29947                 for(var j = 0; j < c.rowspan; j++) {
29948                     if (typeof(ret[rn+j]) == 'undefined') {
29949                         continue; // we have a problem..
29950                     }
29951                     ret[rn+j][cn] = c;
29952                     for(var i = 0; i < c.colspan; i++) {
29953                         ret[rn+j][cn+i] = c;
29954                     }
29955                 }
29956                 
29957                 cn += c.colspan;
29958             }, this);
29959             rn++;
29960         }, this);
29961         
29962         // initalize widths.?
29963         // either all widths or no widths..
29964         if (all_auto) {
29965             this.colWidths[0] = false; // no widths flag.
29966         }
29967         
29968         
29969         return ret;
29970         
29971     },
29972     
29973     
29974     
29975     
29976     mergeRight: function()
29977     {
29978          
29979         // get the contents of the next cell along..
29980         var tr = this.node.closest('tr');
29981         var i = Array.prototype.indexOf.call(tr.childNodes, this.node);
29982         if (i >= tr.childNodes.length - 1) {
29983             return; // no cells on right to merge with.
29984         }
29985         var table = this.toTableArray();
29986         
29987         if (typeof(table[this.cellData.row][this.cellData.col+this.cellData.colspan]) == 'undefined') {
29988             return; // nothing right?
29989         }
29990         var rc = table[this.cellData.row][this.cellData.col+this.cellData.colspan];
29991         // right cell - must be same rowspan and on the same row.
29992         if (rc.rowspan != this.cellData.rowspan || rc.row != this.cellData.row) {
29993             return; // right hand side is not same rowspan.
29994         }
29995         
29996         
29997         
29998         this.node.innerHTML += ' ' + rc.cell.innerHTML;
29999         tr.removeChild(rc.cell);
30000         this.colspan += rc.colspan;
30001         this.node.setAttribute('colspan', this.colspan);
30002
30003         var table = this.toTableArray();
30004         this.normalizeWidths(table);
30005         this.updateWidths(table);
30006     },
30007     
30008     
30009     mergeBelow : function()
30010     {
30011         var table = this.toTableArray();
30012         if (typeof(table[this.cellData.row+this.cellData.rowspan]) == 'undefined') {
30013             return; // no row below
30014         }
30015         if (typeof(table[this.cellData.row+this.cellData.rowspan][this.cellData.col]) == 'undefined') {
30016             return; // nothing right?
30017         }
30018         var rc = table[this.cellData.row+this.cellData.rowspan][this.cellData.col];
30019         
30020         if (rc.colspan != this.cellData.colspan || rc.col != this.cellData.col) {
30021             return; // right hand side is not same rowspan.
30022         }
30023         this.node.innerHTML =  this.node.innerHTML + rc.cell.innerHTML ;
30024         rc.cell.parentNode.removeChild(rc.cell);
30025         this.rowspan += rc.rowspan;
30026         this.node.setAttribute('rowspan', this.rowspan);
30027     },
30028     
30029     split: function()
30030     {
30031         if (this.node.rowSpan < 2 && this.node.colSpan < 2) {
30032             return;
30033         }
30034         var table = this.toTableArray();
30035         var cd = this.cellData;
30036         this.rowspan = 1;
30037         this.colspan = 1;
30038         
30039         for(var r = cd.row; r < cd.row + cd.rowspan; r++) {
30040              
30041             
30042             for(var c = cd.col; c < cd.col + cd.colspan; c++) {
30043                 if (r == cd.row && c == cd.col) {
30044                     this.node.removeAttribute('rowspan');
30045                     this.node.removeAttribute('colspan');
30046                 }
30047                  
30048                 var ntd = this.node.cloneNode(); // which col/row should be 0..
30049                 ntd.removeAttribute('id'); 
30050                 ntd.style.width  = this.colWidths[c];
30051                 ntd.innerHTML = '';
30052                 table[r][c] = { cell : ntd, col : c, row: r , colspan : 1 , rowspan : 1   };
30053             }
30054             
30055         }
30056         this.redrawAllCells(table);
30057         
30058     },
30059     
30060     
30061     
30062     redrawAllCells: function(table)
30063     {
30064         
30065          
30066         var tab = this.node.closest('tr').closest('table');
30067         var ctr = tab.rows[0].parentNode;
30068         Array.from(tab.rows).forEach(function(r, ri){
30069             
30070             Array.from(r.cells).forEach(function(ce, ci){
30071                 ce.parentNode.removeChild(ce);
30072             });
30073             r.parentNode.removeChild(r);
30074         });
30075         for(var r = 0 ; r < table.length; r++) {
30076             var re = tab.rows[r];
30077             
30078             var re = tab.ownerDocument.createElement('tr');
30079             ctr.appendChild(re);
30080             for(var c = 0 ; c < table[r].length; c++) {
30081                 if (table[r][c].cell === false) {
30082                     continue;
30083                 }
30084                 
30085                 re.appendChild(table[r][c].cell);
30086                  
30087                 table[r][c].cell = false;
30088             }
30089         }
30090         
30091     },
30092     updateWidths : function(table)
30093     {
30094         for(var r = 0 ; r < table.length; r++) {
30095            
30096             for(var c = 0 ; c < table[r].length; c++) {
30097                 if (table[r][c].cell === false) {
30098                     continue;
30099                 }
30100                 
30101                 if (this.colWidths[0] != false && table[r][c].colspan < 2) {
30102                     var el = Roo.htmleditor.Block.factory(table[r][c].cell);
30103                     el.width = Math.floor(this.colWidths[c])  +'%';
30104                     el.updateElement(el.node);
30105                 }
30106                 if (this.colWidths[0] != false && table[r][c].colspan > 1) {
30107                     var el = Roo.htmleditor.Block.factory(table[r][c].cell);
30108                     var width = 0;
30109                     for(var i = 0; i < table[r][c].colspan; i ++) {
30110                         width += Math.floor(this.colWidths[c + i]);
30111                     }
30112                     el.width = width  +'%';
30113                     el.updateElement(el.node);
30114                 }
30115                 table[r][c].cell = false; // done
30116             }
30117         }
30118     },
30119     normalizeWidths : function(table)
30120     {
30121         if (this.colWidths[0] === false) {
30122             var nw = 100.0 / this.colWidths.length;
30123             this.colWidths.forEach(function(w,i) {
30124                 this.colWidths[i] = nw;
30125             },this);
30126             return;
30127         }
30128     
30129         var t = 0, missing = [];
30130         
30131         this.colWidths.forEach(function(w,i) {
30132             //if you mix % and
30133             this.colWidths[i] = this.colWidths[i] == '' ? 0 : (this.colWidths[i]+'').replace(/[^0-9]+/g,'')*1;
30134             var add =  this.colWidths[i];
30135             if (add > 0) {
30136                 t+=add;
30137                 return;
30138             }
30139             missing.push(i);
30140             
30141             
30142         },this);
30143         var nc = this.colWidths.length;
30144         if (missing.length) {
30145             var mult = (nc - missing.length) / (1.0 * nc);
30146             var t = mult * t;
30147             var ew = (100 -t) / (1.0 * missing.length);
30148             this.colWidths.forEach(function(w,i) {
30149                 if (w > 0) {
30150                     this.colWidths[i] = w * mult;
30151                     return;
30152                 }
30153                 
30154                 this.colWidths[i] = ew;
30155             }, this);
30156             // have to make up numbers..
30157              
30158         }
30159         // now we should have all the widths..
30160         
30161     
30162     },
30163     
30164     shrinkColumn : function()
30165     {
30166         var table = this.toTableArray();
30167         this.normalizeWidths(table);
30168         var col = this.cellData.col;
30169         var nw = this.colWidths[col] * 0.8;
30170         if (nw < 5) {
30171             return;
30172         }
30173         var otherAdd = (this.colWidths[col]  * 0.2) / (this.colWidths.length -1);
30174         this.colWidths.forEach(function(w,i) {
30175             if (i == col) {
30176                  this.colWidths[i] = nw;
30177                 return;
30178             }
30179             this.colWidths[i] += otherAdd
30180         }, this);
30181         this.updateWidths(table);
30182          
30183     },
30184     growColumn : function()
30185     {
30186         var table = this.toTableArray();
30187         this.normalizeWidths(table);
30188         var col = this.cellData.col;
30189         var nw = this.colWidths[col] * 1.2;
30190         if (nw > 90) {
30191             return;
30192         }
30193         var otherSub = (this.colWidths[col]  * 0.2) / (this.colWidths.length -1);
30194         this.colWidths.forEach(function(w,i) {
30195             if (i == col) {
30196                 this.colWidths[i] = nw;
30197                 return;
30198             }
30199             this.colWidths[i] -= otherSub
30200         }, this);
30201         this.updateWidths(table);
30202          
30203     },
30204     deleteRow : function()
30205     {
30206         // delete this rows 'tr'
30207         // if any of the cells in this row have a rowspan > 1 && row!= this row..
30208         // then reduce the rowspan.
30209         var table = this.toTableArray();
30210         // this.cellData.row;
30211         for (var i =0;i< table[this.cellData.row].length ; i++) {
30212             var c = table[this.cellData.row][i];
30213             if (c.row != this.cellData.row) {
30214                 
30215                 c.rowspan--;
30216                 c.cell.setAttribute('rowspan', c.rowspan);
30217                 continue;
30218             }
30219             if (c.rowspan > 1) {
30220                 c.rowspan--;
30221                 c.cell.setAttribute('rowspan', c.rowspan);
30222             }
30223         }
30224         table.splice(this.cellData.row,1);
30225         this.redrawAllCells(table);
30226         
30227     },
30228     deleteColumn : function()
30229     {
30230         var table = this.toTableArray();
30231         
30232         for (var i =0;i< table.length ; i++) {
30233             var c = table[i][this.cellData.col];
30234             if (c.col != this.cellData.col) {
30235                 table[i][this.cellData.col].colspan--;
30236             } else if (c.colspan > 1) {
30237                 c.colspan--;
30238                 c.cell.setAttribute('colspan', c.colspan);
30239             }
30240             table[i].splice(this.cellData.col,1);
30241         }
30242         
30243         this.redrawAllCells(table);
30244     }
30245     
30246     
30247     
30248     
30249 })
30250
30251 //<script type="text/javascript">
30252
30253 /*
30254  * Based  Ext JS Library 1.1.1
30255  * Copyright(c) 2006-2007, Ext JS, LLC.
30256  * LGPL
30257  *
30258  */
30259  
30260 /**
30261  * @class Roo.HtmlEditorCore
30262  * @extends Roo.Component
30263  * Provides a the editing component for the HTML editors in Roo. (bootstrap and Roo.form)
30264  *
30265  * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
30266  */
30267
30268 Roo.HtmlEditorCore = function(config){
30269     
30270     
30271     Roo.HtmlEditorCore.superclass.constructor.call(this, config);
30272     
30273     
30274     this.addEvents({
30275         /**
30276          * @event initialize
30277          * Fires when the editor is fully initialized (including the iframe)
30278          * @param {Roo.HtmlEditorCore} this
30279          */
30280         initialize: true,
30281         /**
30282          * @event activate
30283          * Fires when the editor is first receives the focus. Any insertion must wait
30284          * until after this event.
30285          * @param {Roo.HtmlEditorCore} this
30286          */
30287         activate: true,
30288          /**
30289          * @event beforesync
30290          * Fires before the textarea is updated with content from the editor iframe. Return false
30291          * to cancel the sync.
30292          * @param {Roo.HtmlEditorCore} this
30293          * @param {String} html
30294          */
30295         beforesync: true,
30296          /**
30297          * @event beforepush
30298          * Fires before the iframe editor is updated with content from the textarea. Return false
30299          * to cancel the push.
30300          * @param {Roo.HtmlEditorCore} this
30301          * @param {String} html
30302          */
30303         beforepush: true,
30304          /**
30305          * @event sync
30306          * Fires when the textarea is updated with content from the editor iframe.
30307          * @param {Roo.HtmlEditorCore} this
30308          * @param {String} html
30309          */
30310         sync: true,
30311          /**
30312          * @event push
30313          * Fires when the iframe editor is updated with content from the textarea.
30314          * @param {Roo.HtmlEditorCore} this
30315          * @param {String} html
30316          */
30317         push: true,
30318         
30319         /**
30320          * @event editorevent
30321          * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
30322          * @param {Roo.HtmlEditorCore} this
30323          */
30324         editorevent: true 
30325          
30326         
30327     });
30328     
30329     // at this point this.owner is set, so we can start working out the whitelisted / blacklisted elements
30330     
30331     // defaults : white / black...
30332     this.applyBlacklists();
30333     
30334     
30335     
30336 };
30337
30338
30339 Roo.extend(Roo.HtmlEditorCore, Roo.Component,  {
30340
30341
30342      /**
30343      * @cfg {Roo.form.HtmlEditor|Roo.bootstrap.HtmlEditor} the owner field 
30344      */
30345     
30346     owner : false,
30347     
30348      /**
30349      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
30350      *                        Roo.resizable.
30351      */
30352     resizable : false,
30353      /**
30354      * @cfg {Number} height (in pixels)
30355      */   
30356     height: 300,
30357    /**
30358      * @cfg {Number} width (in pixels)
30359      */   
30360     width: 500,
30361      /**
30362      * @cfg {boolean} autoClean - default true - loading and saving will remove quite a bit of formating,
30363      *         if you are doing an email editor, this probably needs disabling, it's designed
30364      */
30365     autoClean: true,
30366     
30367     /**
30368      * @cfg {boolean} enableBlocks - default true - if the block editor (table and figure should be enabled)
30369      */
30370     enableBlocks : true,
30371     /**
30372      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
30373      * 
30374      */
30375     stylesheets: false,
30376      /**
30377      * @cfg {String} language default en - language of text (usefull for rtl languages)
30378      * 
30379      */
30380     language: 'en',
30381     
30382     /**
30383      * @cfg {boolean} allowComments - default false - allow comments in HTML source
30384      *          - by default they are stripped - if you are editing email you may need this.
30385      */
30386     allowComments: false,
30387     // id of frame..
30388     frameId: false,
30389     
30390     // private properties
30391     validationEvent : false,
30392     deferHeight: true,
30393     initialized : false,
30394     activated : false,
30395     sourceEditMode : false,
30396     onFocus : Roo.emptyFn,
30397     iframePad:3,
30398     hideMode:'offsets',
30399     
30400     clearUp: true,
30401     
30402     // blacklist + whitelisted elements..
30403     black: false,
30404     white: false,
30405      
30406     bodyCls : '',
30407
30408     
30409     undoManager : false,
30410     /**
30411      * Protected method that will not generally be called directly. It
30412      * is called when the editor initializes the iframe with HTML contents. Override this method if you
30413      * want to change the initialization markup of the iframe (e.g. to add stylesheets).
30414      */
30415     getDocMarkup : function(){
30416         // body styles..
30417         var st = '';
30418         
30419         // inherit styels from page...?? 
30420         if (this.stylesheets === false) {
30421             
30422             Roo.get(document.head).select('style').each(function(node) {
30423                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
30424             });
30425             
30426             Roo.get(document.head).select('link').each(function(node) { 
30427                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
30428             });
30429             
30430         } else if (!this.stylesheets.length) {
30431                 // simple..
30432                 st = '<style type="text/css">' +
30433                     'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
30434                    '</style>';
30435         } else {
30436             for (var i in this.stylesheets) {
30437                 if (typeof(this.stylesheets[i]) != 'string') {
30438                     continue;
30439                 }
30440                 st += '<link rel="stylesheet" href="' + this.stylesheets[i] +'" type="text/css">';
30441             }
30442             
30443         }
30444         
30445         st +=  '<style type="text/css">' +
30446             'IMG { cursor: pointer } ' +
30447         '</style>';
30448         
30449         st += '<meta name="google" content="notranslate">';
30450         
30451         var cls = 'notranslate roo-htmleditor-body';
30452         
30453         if(this.bodyCls.length){
30454             cls += ' ' + this.bodyCls;
30455         }
30456         
30457         return '<html  class="notranslate" translate="no"><head>' + st  +
30458             //<style type="text/css">' +
30459             //'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
30460             //'</style>' +
30461             ' </head><body contenteditable="true" data-enable-grammerly="true" class="' +  cls + '"></body></html>';
30462     },
30463
30464     // private
30465     onRender : function(ct, position)
30466     {
30467         var _t = this;
30468         //Roo.HtmlEditorCore.superclass.onRender.call(this, ct, position);
30469         this.el = this.owner.inputEl ? this.owner.inputEl() : this.owner.el;
30470         
30471         
30472         this.el.dom.style.border = '0 none';
30473         this.el.dom.setAttribute('tabIndex', -1);
30474         this.el.addClass('x-hidden hide');
30475         
30476         
30477         
30478         if(Roo.isIE){ // fix IE 1px bogus margin
30479             this.el.applyStyles('margin-top:-1px;margin-bottom:-1px;')
30480         }
30481        
30482         
30483         this.frameId = Roo.id();
30484         
30485          
30486         
30487         var iframe = this.owner.wrap.createChild({
30488             tag: 'iframe',
30489             cls: 'form-control', // bootstrap..
30490             id: this.frameId,
30491             name: this.frameId,
30492             frameBorder : 'no',
30493             'src' : Roo.SSL_SECURE_URL ? Roo.SSL_SECURE_URL  :  "javascript:false"
30494         }, this.el
30495         );
30496         
30497         
30498         this.iframe = iframe.dom;
30499
30500         this.assignDocWin();
30501         
30502         this.doc.designMode = 'on';
30503        
30504         this.doc.open();
30505         this.doc.write(this.getDocMarkup());
30506         this.doc.close();
30507
30508         
30509         var task = { // must defer to wait for browser to be ready
30510             run : function(){
30511                 //console.log("run task?" + this.doc.readyState);
30512                 this.assignDocWin();
30513                 if(this.doc.body || this.doc.readyState == 'complete'){
30514                     try {
30515                         this.doc.designMode="on";
30516                         
30517                     } catch (e) {
30518                         return;
30519                     }
30520                     Roo.TaskMgr.stop(task);
30521                     this.initEditor.defer(10, this);
30522                 }
30523             },
30524             interval : 10,
30525             duration: 10000,
30526             scope: this
30527         };
30528         Roo.TaskMgr.start(task);
30529
30530     },
30531
30532     // private
30533     onResize : function(w, h)
30534     {
30535          Roo.log('resize: ' +w + ',' + h );
30536         //Roo.HtmlEditorCore.superclass.onResize.apply(this, arguments);
30537         if(!this.iframe){
30538             return;
30539         }
30540         if(typeof w == 'number'){
30541             
30542             this.iframe.style.width = w + 'px';
30543         }
30544         if(typeof h == 'number'){
30545             
30546             this.iframe.style.height = h + 'px';
30547             if(this.doc){
30548                 (this.doc.body || this.doc.documentElement).style.height = (h - (this.iframePad*2)) + 'px';
30549             }
30550         }
30551         
30552     },
30553
30554     /**
30555      * Toggles the editor between standard and source edit mode.
30556      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
30557      */
30558     toggleSourceEdit : function(sourceEditMode){
30559         
30560         this.sourceEditMode = sourceEditMode === true;
30561         
30562         if(this.sourceEditMode){
30563  
30564             Roo.get(this.iframe).addClass(['x-hidden','hide', 'd-none']);     //FIXME - what's the BS styles for these
30565             
30566         }else{
30567             Roo.get(this.iframe).removeClass(['x-hidden','hide', 'd-none']);
30568             //this.iframe.className = '';
30569             this.deferFocus();
30570         }
30571         //this.setSize(this.owner.wrap.getSize());
30572         //this.fireEvent('editmodechange', this, this.sourceEditMode);
30573     },
30574
30575     
30576   
30577
30578     /**
30579      * Protected method that will not generally be called directly. If you need/want
30580      * custom HTML cleanup, this is the method you should override.
30581      * @param {String} html The HTML to be cleaned
30582      * return {String} The cleaned HTML
30583      */
30584     cleanHtml : function(html)
30585     {
30586         html = String(html);
30587         if(html.length > 5){
30588             if(Roo.isSafari){ // strip safari nonsense
30589                 html = html.replace(/\sclass="(?:Apple-style-span|khtml-block-placeholder)"/gi, '');
30590             }
30591         }
30592         if(html == '&nbsp;'){
30593             html = '';
30594         }
30595         return html;
30596     },
30597
30598     /**
30599      * HTML Editor -> Textarea
30600      * Protected method that will not generally be called directly. Syncs the contents
30601      * of the editor iframe with the textarea.
30602      */
30603     syncValue : function()
30604     {
30605         //Roo.log("HtmlEditorCore:syncValue (EDITOR->TEXT)");
30606         if(this.initialized){
30607             
30608             if (this.undoManager) {
30609                 this.undoManager.addEvent();
30610             }
30611
30612             
30613             var bd = (this.doc.body || this.doc.documentElement);
30614            
30615             
30616             var sel = this.win.getSelection();
30617             
30618             var div = document.createElement('div');
30619             div.innerHTML = bd.innerHTML;
30620             var gtx = div.getElementsByClassName('gtx-trans-icon'); // google translate - really annoying and difficult to get rid of.
30621             if (gtx.length > 0) {
30622                 var rm = gtx.item(0).parentNode;
30623                 rm.parentNode.removeChild(rm);
30624             }
30625             
30626            
30627             if (this.enableBlocks) {
30628                 new Roo.htmleditor.FilterBlock({ node : div });
30629             }
30630             
30631             var html = div.innerHTML;
30632             
30633             //?? tidy?
30634             if (this.autoClean) {
30635                 
30636                 new Roo.htmleditor.FilterAttributes({
30637                     node : div,
30638                     attrib_white : [
30639                             'href',
30640                             'src',
30641                             'name',
30642                             'align',
30643                             'colspan',
30644                             'rowspan',
30645                             'data-display',
30646                             'data-width',
30647                             'start' ,
30648                             'style',
30649                             // youtube embed.
30650                             'class',
30651                             'allowfullscreen',
30652                             'frameborder',
30653                             'width',
30654                             'height',
30655                             'alt'
30656                             ],
30657                     attrib_clean : ['href', 'src' ] 
30658                 });
30659                 
30660                 var tidy = new Roo.htmleditor.TidySerializer({
30661                     inner:  true
30662                 });
30663                 html  = tidy.serialize(div);
30664                 
30665             }
30666             
30667             
30668             if(Roo.isSafari){
30669                 var bs = bd.getAttribute('style'); // Safari puts text-align styles on the body element!
30670                 var m = bs ? bs.match(/text-align:(.*?);/i) : false;
30671                 if(m && m[1]){
30672                     html = '<div style="'+m[0]+'">' + html + '</div>';
30673                 }
30674             }
30675             html = this.cleanHtml(html);
30676             // fix up the special chars.. normaly like back quotes in word...
30677             // however we do not want to do this with chinese..
30678             html = html.replace(/[\uD800-\uDBFF][\uDC00-\uDFFF]|[\u0080-\uFFFF]/g, function(match) {
30679                 
30680                 var cc = match.charCodeAt();
30681
30682                 // Get the character value, handling surrogate pairs
30683                 if (match.length == 2) {
30684                     // It's a surrogate pair, calculate the Unicode code point
30685                     var high = match.charCodeAt(0) - 0xD800;
30686                     var low  = match.charCodeAt(1) - 0xDC00;
30687                     cc = (high * 0x400) + low + 0x10000;
30688                 }  else if (
30689                     (cc >= 0x4E00 && cc < 0xA000 ) ||
30690                     (cc >= 0x3400 && cc < 0x4E00 ) ||
30691                     (cc >= 0xf900 && cc < 0xfb00 )
30692                 ) {
30693                         return match;
30694                 }  
30695          
30696                 // No, use a numeric entity. Here we brazenly (and possibly mistakenly)
30697                 return "&#" + cc + ";";
30698                 
30699                 
30700             });
30701             
30702             
30703              
30704             if(this.owner.fireEvent('beforesync', this, html) !== false){
30705                 this.el.dom.value = html;
30706                 this.owner.fireEvent('sync', this, html);
30707             }
30708         }
30709     },
30710
30711     /**
30712      * TEXTAREA -> EDITABLE
30713      * Protected method that will not generally be called directly. Pushes the value of the textarea
30714      * into the iframe editor.
30715      */
30716     pushValue : function()
30717     {
30718         //Roo.log("HtmlEditorCore:pushValue (TEXT->EDITOR)");
30719         if(this.initialized){
30720             var v = this.el.dom.value.trim();
30721             
30722             
30723             if(this.owner.fireEvent('beforepush', this, v) !== false){
30724                 var d = (this.doc.body || this.doc.documentElement);
30725                 d.innerHTML = v;
30726                  
30727                 this.el.dom.value = d.innerHTML;
30728                 this.owner.fireEvent('push', this, v);
30729             }
30730             if (this.autoClean) {
30731                 new Roo.htmleditor.FilterParagraph({node : this.doc.body}); // paragraphs
30732                 new Roo.htmleditor.FilterSpan({node : this.doc.body}); // empty spans
30733             }
30734             if (this.enableBlocks) {
30735                 Roo.htmleditor.Block.initAll(this.doc.body);
30736             }
30737             
30738             this.updateLanguage();
30739             
30740             var lc = this.doc.body.lastChild;
30741             if (lc && lc.nodeType == 1 && lc.getAttribute("contenteditable") == "false") {
30742                 // add an extra line at the end.
30743                 this.doc.body.appendChild(this.doc.createElement('br'));
30744             }
30745             
30746             
30747         }
30748     },
30749
30750     // private
30751     deferFocus : function(){
30752         this.focus.defer(10, this);
30753     },
30754
30755     // doc'ed in Field
30756     focus : function(){
30757         if(this.win && !this.sourceEditMode){
30758             this.win.focus();
30759         }else{
30760             this.el.focus();
30761         }
30762     },
30763     
30764     assignDocWin: function()
30765     {
30766         var iframe = this.iframe;
30767         
30768          if(Roo.isIE){
30769             this.doc = iframe.contentWindow.document;
30770             this.win = iframe.contentWindow;
30771         } else {
30772 //            if (!Roo.get(this.frameId)) {
30773 //                return;
30774 //            }
30775 //            this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
30776 //            this.win = Roo.get(this.frameId).dom.contentWindow;
30777             
30778             if (!Roo.get(this.frameId) && !iframe.contentDocument) {
30779                 return;
30780             }
30781             
30782             this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
30783             this.win = (iframe.contentWindow || Roo.get(this.frameId).dom.contentWindow);
30784         }
30785     },
30786     
30787     // private
30788     initEditor : function(){
30789         //console.log("INIT EDITOR");
30790         this.assignDocWin();
30791         
30792         
30793         
30794         this.doc.designMode="on";
30795         this.doc.open();
30796         this.doc.write(this.getDocMarkup());
30797         this.doc.close();
30798         
30799         var dbody = (this.doc.body || this.doc.documentElement);
30800         //var ss = this.el.getStyles('font-size', 'font-family', 'background-image', 'background-repeat');
30801         // this copies styles from the containing element into thsi one..
30802         // not sure why we need all of this..
30803         //var ss = this.el.getStyles('font-size', 'background-image', 'background-repeat');
30804         
30805         //var ss = this.el.getStyles( 'background-image', 'background-repeat');
30806         //ss['background-attachment'] = 'fixed'; // w3c
30807         dbody.bgProperties = 'fixed'; // ie
30808         dbody.setAttribute("translate", "no");
30809         
30810         //Roo.DomHelper.applyStyles(dbody, ss);
30811         Roo.EventManager.on(this.doc, {
30812              
30813             'mouseup': this.onEditorEvent,
30814             'dblclick': this.onEditorEvent,
30815             'click': this.onEditorEvent,
30816             'keyup': this.onEditorEvent,
30817             
30818             buffer:100,
30819             scope: this
30820         });
30821         Roo.EventManager.on(this.doc, {
30822             'paste': this.onPasteEvent,
30823             scope : this
30824         });
30825         if(Roo.isGecko){
30826             Roo.EventManager.on(this.doc, 'keypress', this.mozKeyPress, this);
30827         }
30828         //??? needed???
30829         if(Roo.isIE || Roo.isSafari || Roo.isOpera){
30830             Roo.EventManager.on(this.doc, 'keydown', this.fixKeys, this);
30831         }
30832         this.initialized = true;
30833
30834         
30835         // initialize special key events - enter
30836         new Roo.htmleditor.KeyEnter({core : this});
30837         
30838          
30839         
30840         this.owner.fireEvent('initialize', this);
30841         this.pushValue();
30842     },
30843     // this is to prevent a href clicks resulting in a redirect?
30844    
30845     onPasteEvent : function(e,v)
30846     {
30847         // I think we better assume paste is going to be a dirty load of rubish from word..
30848         
30849         // even pasting into a 'email version' of this widget will have to clean up that mess.
30850         var cd = (e.browserEvent.clipboardData || window.clipboardData);
30851         
30852         // check what type of paste - if it's an image, then handle it differently.
30853         if (cd.files && cd.files.length > 0) {
30854             // pasting images?
30855             var urlAPI = (window.createObjectURL && window) || 
30856                 (window.URL && URL.revokeObjectURL && URL) || 
30857                 (window.webkitURL && webkitURL);
30858     
30859             var url = urlAPI.createObjectURL( cd.files[0]);
30860             this.insertAtCursor('<img src=" + url + ">');
30861             return false;
30862         }
30863         if (cd.types.indexOf('text/html') < 0 ) {
30864             return false;
30865         }
30866         var images = [];
30867         var html = cd.getData('text/html'); // clipboard event
30868         if (cd.types.indexOf('text/rtf') > -1) {
30869             var parser = new Roo.rtf.Parser(cd.getData('text/rtf'));
30870             images = parser.doc ? parser.doc.getElementsByType('pict') : [];
30871         }
30872         //Roo.log(images);
30873         //Roo.log(imgs);
30874         // fixme..
30875         images = images.filter(function(g) { return !g.path.match(/^rtf\/(head|pgdsctbl|listtable|footerf)/); }) // ignore headers/footers etc.
30876                        .map(function(g) { return g.toDataURL(); })
30877                        .filter(function(g) { return g != 'about:blank'; });
30878         
30879         //Roo.log(html);
30880         html = this.cleanWordChars(html);
30881         
30882         var d = (new DOMParser().parseFromString(html, 'text/html')).body;
30883         
30884         
30885         var sn = this.getParentElement();
30886         // check if d contains a table, and prevent nesting??
30887         //Roo.log(d.getElementsByTagName('table'));
30888         //Roo.log(sn);
30889         //Roo.log(sn.closest('table'));
30890         if (d.getElementsByTagName('table').length && sn && sn.closest('table')) {
30891             e.preventDefault();
30892             this.insertAtCursor("You can not nest tables");
30893             //Roo.log("prevent?"); // fixme - 
30894             return false;
30895         }
30896         
30897         
30898         
30899         if (images.length > 0) {
30900             // replace all v:imagedata - with img.
30901             var ar = Array.from(d.getElementsByTagName('v:imagedata'));
30902             Roo.each(ar, function(node) {
30903                 node.parentNode.insertBefore(d.ownerDocument.createElement('img'), node );
30904                 node.parentNode.removeChild(node);
30905             });
30906             
30907             
30908             Roo.each(d.getElementsByTagName('img'), function(img, i) {
30909                 img.setAttribute('src', images[i]);
30910             });
30911         }
30912         if (this.autoClean) {
30913             new Roo.htmleditor.FilterWord({ node : d });
30914             
30915             new Roo.htmleditor.FilterStyleToTag({ node : d });
30916             new Roo.htmleditor.FilterAttributes({
30917                 node : d,
30918                 attrib_white : ['href', 'src', 'name', 'align', 'colspan', 'rowspan', 'data-display', 'data-width', 'start'],
30919                 attrib_clean : ['href', 'src' ] 
30920             });
30921             new Roo.htmleditor.FilterBlack({ node : d, tag : this.black});
30922             // should be fonts..
30923             new Roo.htmleditor.FilterKeepChildren({node : d, tag : [ 'FONT', ':' ]} );
30924             new Roo.htmleditor.FilterParagraph({ node : d });
30925             new Roo.htmleditor.FilterSpan({ node : d });
30926             new Roo.htmleditor.FilterLongBr({ node : d });
30927             new Roo.htmleditor.FilterComment({ node : d });
30928             
30929             
30930         }
30931         if (this.enableBlocks) {
30932                 
30933             Array.from(d.getElementsByTagName('img')).forEach(function(img) {
30934                 if (img.closest('figure')) { // assume!! that it's aready
30935                     return;
30936                 }
30937                 var fig  = new Roo.htmleditor.BlockFigure({
30938                     image_src  : img.src
30939                 });
30940                 fig.updateElement(img); // replace it..
30941                 
30942             });
30943         }
30944         
30945         
30946         this.insertAtCursor(d.innerHTML.replace(/&nbsp;/g,' '));
30947         if (this.enableBlocks) {
30948             Roo.htmleditor.Block.initAll(this.doc.body);
30949         }
30950          
30951         
30952         e.preventDefault();
30953         return false;
30954         // default behaveiour should be our local cleanup paste? (optional?)
30955         // for simple editor - we want to hammer the paste and get rid of everything... - so over-rideable..
30956         //this.owner.fireEvent('paste', e, v);
30957     },
30958     // private
30959     onDestroy : function(){
30960         
30961         
30962         
30963         if(this.rendered){
30964             
30965             //for (var i =0; i < this.toolbars.length;i++) {
30966             //    // fixme - ask toolbars for heights?
30967             //    this.toolbars[i].onDestroy();
30968            // }
30969             
30970             //this.wrap.dom.innerHTML = '';
30971             //this.wrap.remove();
30972         }
30973     },
30974
30975     // private
30976     onFirstFocus : function(){
30977         
30978         this.assignDocWin();
30979         this.undoManager = new Roo.lib.UndoManager(100,(this.doc.body || this.doc.documentElement));
30980         
30981         this.activated = true;
30982          
30983     
30984         if(Roo.isGecko){ // prevent silly gecko errors
30985             this.win.focus();
30986             var s = this.win.getSelection();
30987             if(!s.focusNode || s.focusNode.nodeType != 3){
30988                 var r = s.getRangeAt(0);
30989                 r.selectNodeContents((this.doc.body || this.doc.documentElement));
30990                 r.collapse(true);
30991                 this.deferFocus();
30992             }
30993             try{
30994                 this.execCmd('useCSS', true);
30995                 this.execCmd('styleWithCSS', false);
30996             }catch(e){}
30997         }
30998         this.owner.fireEvent('activate', this);
30999     },
31000
31001     // private
31002     adjustFont: function(btn){
31003         var adjust = btn.cmd == 'increasefontsize' ? 1 : -1;
31004         //if(Roo.isSafari){ // safari
31005         //    adjust *= 2;
31006        // }
31007         var v = parseInt(this.doc.queryCommandValue('FontSize')|| 3, 10);
31008         if(Roo.isSafari){ // safari
31009             var sm = { 10 : 1, 13: 2, 16:3, 18:4, 24: 5, 32:6, 48: 7 };
31010             v =  (v < 10) ? 10 : v;
31011             v =  (v > 48) ? 48 : v;
31012             v = typeof(sm[v]) == 'undefined' ? 1 : sm[v];
31013             
31014         }
31015         
31016         
31017         v = Math.max(1, v+adjust);
31018         
31019         this.execCmd('FontSize', v  );
31020     },
31021
31022     onEditorEvent : function(e)
31023     {
31024          
31025         
31026         if (e && (e.ctrlKey || e.metaKey) && e.keyCode === 90) {
31027             return; // we do not handle this.. (undo manager does..)
31028         }
31029         // in theory this detects if the last element is not a br, then we try and do that.
31030         // its so clicking in space at bottom triggers adding a br and moving the cursor.
31031         if (e &&
31032             e.target.nodeName == 'BODY' &&
31033             e.type == "mouseup" &&
31034             this.doc.body.lastChild
31035            ) {
31036             var lc = this.doc.body.lastChild;
31037             // gtx-trans is google translate plugin adding crap.
31038             while ((lc.nodeType == 3 && lc.nodeValue == '') || lc.id == 'gtx-trans') {
31039                 lc = lc.previousSibling;
31040             }
31041             if (lc.nodeType == 1 && lc.nodeName != 'BR') {
31042             // if last element is <BR> - then dont do anything.
31043             
31044                 var ns = this.doc.createElement('br');
31045                 this.doc.body.appendChild(ns);
31046                 range = this.doc.createRange();
31047                 range.setStartAfter(ns);
31048                 range.collapse(true);
31049                 var sel = this.win.getSelection();
31050                 sel.removeAllRanges();
31051                 sel.addRange(range);
31052             }
31053         }
31054         
31055         
31056         
31057         this.fireEditorEvent(e);
31058       //  this.updateToolbar();
31059         this.syncValue(); //we can not sync so often.. sync cleans, so this breaks stuff
31060     },
31061     
31062     fireEditorEvent: function(e)
31063     {
31064         this.owner.fireEvent('editorevent', this, e);
31065     },
31066
31067     insertTag : function(tg)
31068     {
31069         // could be a bit smarter... -> wrap the current selected tRoo..
31070         if (tg.toLowerCase() == 'span' ||
31071             tg.toLowerCase() == 'code' ||
31072             tg.toLowerCase() == 'sup' ||
31073             tg.toLowerCase() == 'sub' 
31074             ) {
31075             
31076             range = this.createRange(this.getSelection());
31077             var wrappingNode = this.doc.createElement(tg.toLowerCase());
31078             wrappingNode.appendChild(range.extractContents());
31079             range.insertNode(wrappingNode);
31080
31081             return;
31082             
31083             
31084             
31085         }
31086         this.execCmd("formatblock",   tg);
31087         this.undoManager.addEvent(); 
31088     },
31089     
31090     insertText : function(txt)
31091     {
31092         
31093         
31094         var range = this.createRange();
31095         range.deleteContents();
31096                //alert(Sender.getAttribute('label'));
31097                
31098         range.insertNode(this.doc.createTextNode(txt));
31099         this.undoManager.addEvent();
31100     } ,
31101     
31102      
31103
31104     /**
31105      * Executes a Midas editor command on the editor document and performs necessary focus and
31106      * toolbar updates. <b>This should only be called after the editor is initialized.</b>
31107      * @param {String} cmd The Midas command
31108      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
31109      */
31110     relayCmd : function(cmd, value)
31111     {
31112         
31113         switch (cmd) {
31114             case 'justifyleft':
31115             case 'justifyright':
31116             case 'justifycenter':
31117                 // if we are in a cell, then we will adjust the
31118                 var n = this.getParentElement();
31119                 var td = n.closest('td');
31120                 if (td) {
31121                     var bl = Roo.htmleditor.Block.factory(td);
31122                     bl.textAlign = cmd.replace('justify','');
31123                     bl.updateElement();
31124                     this.owner.fireEvent('editorevent', this);
31125                     return;
31126                 }
31127                 this.execCmd('styleWithCSS', true); // 
31128                 break;
31129             case 'bold':
31130             case 'italic':
31131                 // if there is no selection, then we insert, and set the curson inside it..
31132                 this.execCmd('styleWithCSS', false); 
31133                 break;
31134                 
31135         
31136             default:
31137                 break;
31138         }
31139         
31140         
31141         this.win.focus();
31142         this.execCmd(cmd, value);
31143         this.owner.fireEvent('editorevent', this);
31144         //this.updateToolbar();
31145         this.owner.deferFocus();
31146     },
31147
31148     /**
31149      * Executes a Midas editor command directly on the editor document.
31150      * For visual commands, you should use {@link #relayCmd} instead.
31151      * <b>This should only be called after the editor is initialized.</b>
31152      * @param {String} cmd The Midas command
31153      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
31154      */
31155     execCmd : function(cmd, value){
31156         this.doc.execCommand(cmd, false, value === undefined ? null : value);
31157         this.syncValue();
31158     },
31159  
31160  
31161    
31162     /**
31163      * Inserts the passed text at the current cursor position. Note: the editor must be initialized and activated
31164      * to insert tRoo.
31165      * @param {String} text | dom node.. 
31166      */
31167     insertAtCursor : function(text)
31168     {
31169         
31170         if(!this.activated){
31171             return;
31172         }
31173          
31174         if(Roo.isGecko || Roo.isOpera || Roo.isSafari){
31175             this.win.focus();
31176             
31177             
31178             // from jquery ui (MIT licenced)
31179             var range, node;
31180             var win = this.win;
31181             
31182             if (win.getSelection && win.getSelection().getRangeAt) {
31183                 
31184                 // delete the existing?
31185                 
31186                 this.createRange(this.getSelection()).deleteContents();
31187                 range = win.getSelection().getRangeAt(0);
31188                 node = typeof(text) == 'string' ? range.createContextualFragment(text) : text;
31189                 range.insertNode(node);
31190                 range = range.cloneRange();
31191                 range.collapse(false);
31192                  
31193                 win.getSelection().removeAllRanges();
31194                 win.getSelection().addRange(range);
31195                 
31196                 
31197                 
31198             } else if (win.document.selection && win.document.selection.createRange) {
31199                 // no firefox support
31200                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
31201                 win.document.selection.createRange().pasteHTML(txt);
31202             
31203             } else {
31204                 // no firefox support
31205                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
31206                 this.execCmd('InsertHTML', txt);
31207             } 
31208             this.syncValue();
31209             
31210             this.deferFocus();
31211         }
31212     },
31213  // private
31214     mozKeyPress : function(e){
31215         if(e.ctrlKey){
31216             var c = e.getCharCode(), cmd;
31217           
31218             if(c > 0){
31219                 c = String.fromCharCode(c).toLowerCase();
31220                 switch(c){
31221                     case 'b':
31222                         cmd = 'bold';
31223                         break;
31224                     case 'i':
31225                         cmd = 'italic';
31226                         break;
31227                     
31228                     case 'u':
31229                         cmd = 'underline';
31230                         break;
31231                     
31232                     //case 'v':
31233                       //  this.cleanUpPaste.defer(100, this);
31234                       //  return;
31235                         
31236                 }
31237                 if(cmd){
31238                     
31239                     this.relayCmd(cmd);
31240                     //this.win.focus();
31241                     //this.execCmd(cmd);
31242                     //this.deferFocus();
31243                     e.preventDefault();
31244                 }
31245                 
31246             }
31247         }
31248     },
31249
31250     // private
31251     fixKeys : function(){ // load time branching for fastest keydown performance
31252         
31253         
31254         if(Roo.isIE){
31255             return function(e){
31256                 var k = e.getKey(), r;
31257                 if(k == e.TAB){
31258                     e.stopEvent();
31259                     r = this.doc.selection.createRange();
31260                     if(r){
31261                         r.collapse(true);
31262                         r.pasteHTML('&#160;&#160;&#160;&#160;');
31263                         this.deferFocus();
31264                     }
31265                     return;
31266                 }
31267                 /// this is handled by Roo.htmleditor.KeyEnter
31268                  /*
31269                 if(k == e.ENTER){
31270                     r = this.doc.selection.createRange();
31271                     if(r){
31272                         var target = r.parentElement();
31273                         if(!target || target.tagName.toLowerCase() != 'li'){
31274                             e.stopEvent();
31275                             r.pasteHTML('<br/>');
31276                             r.collapse(false);
31277                             r.select();
31278                         }
31279                     }
31280                 }
31281                 */
31282                 //if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
31283                 //    this.cleanUpPaste.defer(100, this);
31284                 //    return;
31285                 //}
31286                 
31287                 
31288             };
31289         }else if(Roo.isOpera){
31290             return function(e){
31291                 var k = e.getKey();
31292                 if(k == e.TAB){
31293                     e.stopEvent();
31294                     this.win.focus();
31295                     this.execCmd('InsertHTML','&#160;&#160;&#160;&#160;');
31296                     this.deferFocus();
31297                 }
31298                
31299                 //if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
31300                 //    this.cleanUpPaste.defer(100, this);
31301                  //   return;
31302                 //}
31303                 
31304             };
31305         }else if(Roo.isSafari){
31306             return function(e){
31307                 var k = e.getKey();
31308                 
31309                 if(k == e.TAB){
31310                     e.stopEvent();
31311                     this.execCmd('InsertText','\t');
31312                     this.deferFocus();
31313                     return;
31314                 }
31315                  this.mozKeyPress(e);
31316                 
31317                //if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
31318                  //   this.cleanUpPaste.defer(100, this);
31319                  //   return;
31320                // }
31321                 
31322              };
31323         }
31324     }(),
31325     
31326     getAllAncestors: function()
31327     {
31328         var p = this.getSelectedNode();
31329         var a = [];
31330         if (!p) {
31331             a.push(p); // push blank onto stack..
31332             p = this.getParentElement();
31333         }
31334         
31335         
31336         while (p && (p.nodeType == 1) && (p.tagName.toLowerCase() != 'body')) {
31337             a.push(p);
31338             p = p.parentNode;
31339         }
31340         a.push(this.doc.body);
31341         return a;
31342     },
31343     lastSel : false,
31344     lastSelNode : false,
31345     
31346     
31347     getSelection : function() 
31348     {
31349         this.assignDocWin();
31350         return Roo.lib.Selection.wrap(Roo.isIE ? this.doc.selection : this.win.getSelection(), this.doc);
31351     },
31352     /**
31353      * Select a dom node
31354      * @param {DomElement} node the node to select
31355      */
31356     selectNode : function(node, collapse)
31357     {
31358         var nodeRange = node.ownerDocument.createRange();
31359         try {
31360             nodeRange.selectNode(node);
31361         } catch (e) {
31362             nodeRange.selectNodeContents(node);
31363         }
31364         if (collapse === true) {
31365             nodeRange.collapse(true);
31366         }
31367         //
31368         var s = this.win.getSelection();
31369         s.removeAllRanges();
31370         s.addRange(nodeRange);
31371     },
31372     
31373     getSelectedNode: function() 
31374     {
31375         // this may only work on Gecko!!!
31376         
31377         // should we cache this!!!!
31378         
31379          
31380          
31381         var range = this.createRange(this.getSelection()).cloneRange();
31382         
31383         if (Roo.isIE) {
31384             var parent = range.parentElement();
31385             while (true) {
31386                 var testRange = range.duplicate();
31387                 testRange.moveToElementText(parent);
31388                 if (testRange.inRange(range)) {
31389                     break;
31390                 }
31391                 if ((parent.nodeType != 1) || (parent.tagName.toLowerCase() == 'body')) {
31392                     break;
31393                 }
31394                 parent = parent.parentElement;
31395             }
31396             return parent;
31397         }
31398         
31399         // is ancestor a text element.
31400         var ac =  range.commonAncestorContainer;
31401         if (ac.nodeType == 3) {
31402             ac = ac.parentNode;
31403         }
31404         
31405         var ar = ac.childNodes;
31406          
31407         var nodes = [];
31408         var other_nodes = [];
31409         var has_other_nodes = false;
31410         for (var i=0;i<ar.length;i++) {
31411             if ((ar[i].nodeType == 3) && (!ar[i].data.length)) { // empty text ? 
31412                 continue;
31413             }
31414             // fullly contained node.
31415             
31416             if (this.rangeIntersectsNode(range,ar[i]) && this.rangeCompareNode(range,ar[i]) == 3) {
31417                 nodes.push(ar[i]);
31418                 continue;
31419             }
31420             
31421             // probably selected..
31422             if ((ar[i].nodeType == 1) && this.rangeIntersectsNode(range,ar[i]) && (this.rangeCompareNode(range,ar[i]) > 0)) {
31423                 other_nodes.push(ar[i]);
31424                 continue;
31425             }
31426             // outer..
31427             if (!this.rangeIntersectsNode(range,ar[i])|| (this.rangeCompareNode(range,ar[i]) == 0))  {
31428                 continue;
31429             }
31430             
31431             
31432             has_other_nodes = true;
31433         }
31434         if (!nodes.length && other_nodes.length) {
31435             nodes= other_nodes;
31436         }
31437         if (has_other_nodes || !nodes.length || (nodes.length > 1)) {
31438             return false;
31439         }
31440         
31441         return nodes[0];
31442     },
31443     
31444     
31445     createRange: function(sel)
31446     {
31447         // this has strange effects when using with 
31448         // top toolbar - not sure if it's a great idea.
31449         //this.editor.contentWindow.focus();
31450         if (typeof sel != "undefined") {
31451             try {
31452                 return sel.getRangeAt ? sel.getRangeAt(0) : sel.createRange();
31453             } catch(e) {
31454                 return this.doc.createRange();
31455             }
31456         } else {
31457             return this.doc.createRange();
31458         }
31459     },
31460     getParentElement: function()
31461     {
31462         
31463         this.assignDocWin();
31464         var sel = Roo.isIE ? this.doc.selection : this.win.getSelection();
31465         
31466         var range = this.createRange(sel);
31467          
31468         try {
31469             var p = range.commonAncestorContainer;
31470             while (p.nodeType == 3) { // text node
31471                 p = p.parentNode;
31472             }
31473             return p;
31474         } catch (e) {
31475             return null;
31476         }
31477     
31478     },
31479     /***
31480      *
31481      * Range intersection.. the hard stuff...
31482      *  '-1' = before
31483      *  '0' = hits..
31484      *  '1' = after.
31485      *         [ -- selected range --- ]
31486      *   [fail]                        [fail]
31487      *
31488      *    basically..
31489      *      if end is before start or  hits it. fail.
31490      *      if start is after end or hits it fail.
31491      *
31492      *   if either hits (but other is outside. - then it's not 
31493      *   
31494      *    
31495      **/
31496     
31497     
31498     // @see http://www.thismuchiknow.co.uk/?p=64.
31499     rangeIntersectsNode : function(range, node)
31500     {
31501         var nodeRange = node.ownerDocument.createRange();
31502         try {
31503             nodeRange.selectNode(node);
31504         } catch (e) {
31505             nodeRange.selectNodeContents(node);
31506         }
31507     
31508         var rangeStartRange = range.cloneRange();
31509         rangeStartRange.collapse(true);
31510     
31511         var rangeEndRange = range.cloneRange();
31512         rangeEndRange.collapse(false);
31513     
31514         var nodeStartRange = nodeRange.cloneRange();
31515         nodeStartRange.collapse(true);
31516     
31517         var nodeEndRange = nodeRange.cloneRange();
31518         nodeEndRange.collapse(false);
31519     
31520         return rangeStartRange.compareBoundaryPoints(
31521                  Range.START_TO_START, nodeEndRange) == -1 &&
31522                rangeEndRange.compareBoundaryPoints(
31523                  Range.START_TO_START, nodeStartRange) == 1;
31524         
31525          
31526     },
31527     rangeCompareNode : function(range, node)
31528     {
31529         var nodeRange = node.ownerDocument.createRange();
31530         try {
31531             nodeRange.selectNode(node);
31532         } catch (e) {
31533             nodeRange.selectNodeContents(node);
31534         }
31535         
31536         
31537         range.collapse(true);
31538     
31539         nodeRange.collapse(true);
31540      
31541         var ss = range.compareBoundaryPoints( Range.START_TO_START, nodeRange);
31542         var ee = range.compareBoundaryPoints(  Range.END_TO_END, nodeRange);
31543          
31544         //Roo.log(node.tagName + ': ss='+ss +', ee='+ee)
31545         
31546         var nodeIsBefore   =  ss == 1;
31547         var nodeIsAfter    = ee == -1;
31548         
31549         if (nodeIsBefore && nodeIsAfter) {
31550             return 0; // outer
31551         }
31552         if (!nodeIsBefore && nodeIsAfter) {
31553             return 1; //right trailed.
31554         }
31555         
31556         if (nodeIsBefore && !nodeIsAfter) {
31557             return 2;  // left trailed.
31558         }
31559         // fully contined.
31560         return 3;
31561     },
31562  
31563     cleanWordChars : function(input) {// change the chars to hex code
31564         
31565        var swapCodes  = [ 
31566             [    8211, "&#8211;" ], 
31567             [    8212, "&#8212;" ], 
31568             [    8216,  "'" ],  
31569             [    8217, "'" ],  
31570             [    8220, '"' ],  
31571             [    8221, '"' ],  
31572             [    8226, "*" ],  
31573             [    8230, "..." ]
31574         ]; 
31575         var output = input;
31576         Roo.each(swapCodes, function(sw) { 
31577             var swapper = new RegExp("\\u" + sw[0].toString(16), "g"); // hex codes
31578             
31579             output = output.replace(swapper, sw[1]);
31580         });
31581         
31582         return output;
31583     },
31584     
31585      
31586     
31587         
31588     
31589     cleanUpChild : function (node)
31590     {
31591         
31592         new Roo.htmleditor.FilterComment({node : node});
31593         new Roo.htmleditor.FilterAttributes({
31594                 node : node,
31595                 attrib_black : this.ablack,
31596                 attrib_clean : this.aclean,
31597                 style_white : this.cwhite,
31598                 style_black : this.cblack
31599         });
31600         new Roo.htmleditor.FilterBlack({ node : node, tag : this.black});
31601         new Roo.htmleditor.FilterKeepChildren({node : node, tag : this.tag_remove} );
31602          
31603         
31604     },
31605     
31606     /**
31607      * Clean up MS wordisms...
31608      * @deprecated - use filter directly
31609      */
31610     cleanWord : function(node)
31611     {
31612         new Roo.htmleditor.FilterWord({ node : node ? node : this.doc.body });
31613         new Roo.htmleditor.FilterKeepChildren({node : node ? node : this.doc.body, tag : [ 'FONT', ':' ]} );
31614         
31615     },
31616    
31617     
31618     /**
31619
31620      * @deprecated - use filters
31621      */
31622     cleanTableWidths : function(node)
31623     {
31624         new Roo.htmleditor.FilterTableWidth({ node : node ? node : this.doc.body});
31625         
31626  
31627     },
31628     
31629      
31630         
31631     applyBlacklists : function()
31632     {
31633         var w = typeof(this.owner.white) != 'undefined' && this.owner.white ? this.owner.white  : [];
31634         var b = typeof(this.owner.black) != 'undefined' && this.owner.black ? this.owner.black :  [];
31635         
31636         this.aclean = typeof(this.owner.aclean) != 'undefined' && this.owner.aclean ? this.owner.aclean :  Roo.HtmlEditorCore.aclean;
31637         this.ablack = typeof(this.owner.ablack) != 'undefined' && this.owner.ablack ? this.owner.ablack :  Roo.HtmlEditorCore.ablack;
31638         this.tag_remove = typeof(this.owner.tag_remove) != 'undefined' && this.owner.tag_remove ? this.owner.tag_remove :  Roo.HtmlEditorCore.tag_remove;
31639         
31640         this.white = [];
31641         this.black = [];
31642         Roo.each(Roo.HtmlEditorCore.white, function(tag) {
31643             if (b.indexOf(tag) > -1) {
31644                 return;
31645             }
31646             this.white.push(tag);
31647             
31648         }, this);
31649         
31650         Roo.each(w, function(tag) {
31651             if (b.indexOf(tag) > -1) {
31652                 return;
31653             }
31654             if (this.white.indexOf(tag) > -1) {
31655                 return;
31656             }
31657             this.white.push(tag);
31658             
31659         }, this);
31660         
31661         
31662         Roo.each(Roo.HtmlEditorCore.black, function(tag) {
31663             if (w.indexOf(tag) > -1) {
31664                 return;
31665             }
31666             this.black.push(tag);
31667             
31668         }, this);
31669         
31670         Roo.each(b, function(tag) {
31671             if (w.indexOf(tag) > -1) {
31672                 return;
31673             }
31674             if (this.black.indexOf(tag) > -1) {
31675                 return;
31676             }
31677             this.black.push(tag);
31678             
31679         }, this);
31680         
31681         
31682         w = typeof(this.owner.cwhite) != 'undefined' && this.owner.cwhite ? this.owner.cwhite  : [];
31683         b = typeof(this.owner.cblack) != 'undefined' && this.owner.cblack ? this.owner.cblack :  [];
31684         
31685         this.cwhite = [];
31686         this.cblack = [];
31687         Roo.each(Roo.HtmlEditorCore.cwhite, function(tag) {
31688             if (b.indexOf(tag) > -1) {
31689                 return;
31690             }
31691             this.cwhite.push(tag);
31692             
31693         }, this);
31694         
31695         Roo.each(w, function(tag) {
31696             if (b.indexOf(tag) > -1) {
31697                 return;
31698             }
31699             if (this.cwhite.indexOf(tag) > -1) {
31700                 return;
31701             }
31702             this.cwhite.push(tag);
31703             
31704         }, this);
31705         
31706         
31707         Roo.each(Roo.HtmlEditorCore.cblack, function(tag) {
31708             if (w.indexOf(tag) > -1) {
31709                 return;
31710             }
31711             this.cblack.push(tag);
31712             
31713         }, this);
31714         
31715         Roo.each(b, function(tag) {
31716             if (w.indexOf(tag) > -1) {
31717                 return;
31718             }
31719             if (this.cblack.indexOf(tag) > -1) {
31720                 return;
31721             }
31722             this.cblack.push(tag);
31723             
31724         }, this);
31725     },
31726     
31727     setStylesheets : function(stylesheets)
31728     {
31729         if(typeof(stylesheets) == 'string'){
31730             Roo.get(this.iframe.contentDocument.head).createChild({
31731                 tag : 'link',
31732                 rel : 'stylesheet',
31733                 type : 'text/css',
31734                 href : stylesheets
31735             });
31736             
31737             return;
31738         }
31739         var _this = this;
31740      
31741         Roo.each(stylesheets, function(s) {
31742             if(!s.length){
31743                 return;
31744             }
31745             
31746             Roo.get(_this.iframe.contentDocument.head).createChild({
31747                 tag : 'link',
31748                 rel : 'stylesheet',
31749                 type : 'text/css',
31750                 href : s
31751             });
31752         });
31753
31754         
31755     },
31756     
31757     
31758     updateLanguage : function()
31759     {
31760         if (!this.iframe || !this.iframe.contentDocument) {
31761             return;
31762         }
31763         Roo.get(this.iframe.contentDocument.body).attr("lang", this.language);
31764     },
31765     
31766     
31767     removeStylesheets : function()
31768     {
31769         var _this = this;
31770         
31771         Roo.each(Roo.get(_this.iframe.contentDocument.head).select('link[rel=stylesheet]', true).elements, function(s){
31772             s.remove();
31773         });
31774     },
31775     
31776     setStyle : function(style)
31777     {
31778         Roo.get(this.iframe.contentDocument.head).createChild({
31779             tag : 'style',
31780             type : 'text/css',
31781             html : style
31782         });
31783
31784         return;
31785     }
31786     
31787     // hide stuff that is not compatible
31788     /**
31789      * @event blur
31790      * @hide
31791      */
31792     /**
31793      * @event change
31794      * @hide
31795      */
31796     /**
31797      * @event focus
31798      * @hide
31799      */
31800     /**
31801      * @event specialkey
31802      * @hide
31803      */
31804     /**
31805      * @cfg {String} fieldClass @hide
31806      */
31807     /**
31808      * @cfg {String} focusClass @hide
31809      */
31810     /**
31811      * @cfg {String} autoCreate @hide
31812      */
31813     /**
31814      * @cfg {String} inputType @hide
31815      */
31816     /**
31817      * @cfg {String} invalidClass @hide
31818      */
31819     /**
31820      * @cfg {String} invalidText @hide
31821      */
31822     /**
31823      * @cfg {String} msgFx @hide
31824      */
31825     /**
31826      * @cfg {String} validateOnBlur @hide
31827      */
31828 });
31829
31830 Roo.HtmlEditorCore.white = [
31831         'AREA', 'BR', 'IMG', 'INPUT', 'HR', 'WBR',
31832         
31833        'ADDRESS', 'BLOCKQUOTE', 'CENTER', 'DD',      'DIR',       'DIV', 
31834        'DL',      'DT',         'H1',     'H2',      'H3',        'H4', 
31835        'H5',      'H6',         'HR',     'ISINDEX', 'LISTING',   'MARQUEE', 
31836        'MENU',    'MULTICOL',   'OL',     'P',       'PLAINTEXT', 'PRE', 
31837        'TABLE',   'UL',         'XMP', 
31838        
31839        'CAPTION', 'COL', 'COLGROUP', 'TBODY', 'TD', 'TFOOT', 'TH', 
31840       'THEAD',   'TR', 
31841      
31842       'DIR', 'MENU', 'OL', 'UL', 'DL',
31843        
31844       'EMBED',  'OBJECT'
31845 ];
31846
31847
31848 Roo.HtmlEditorCore.black = [
31849     //    'embed',  'object', // enable - backend responsiblity to clean thiese
31850         'APPLET', // 
31851         'BASE',   'BASEFONT', 'BGSOUND', 'BLINK',  'BODY', 
31852         'FRAME',  'FRAMESET', 'HEAD',    'HTML',   'ILAYER', 
31853         'IFRAME', 'LAYER',  'LINK',     'META',    'OBJECT',   
31854         'SCRIPT', 'STYLE' ,'TITLE',  'XML',
31855         //'FONT' // CLEAN LATER..
31856         'COLGROUP', 'COL'   // messy tables.
31857         
31858         
31859 ];
31860 Roo.HtmlEditorCore.clean = [ // ?? needed???
31861      'SCRIPT', 'STYLE', 'TITLE', 'XML'
31862 ];
31863 Roo.HtmlEditorCore.tag_remove = [
31864     'FONT', 'TBODY'  
31865 ];
31866 // attributes..
31867
31868 Roo.HtmlEditorCore.ablack = [
31869     'on'
31870 ];
31871     
31872 Roo.HtmlEditorCore.aclean = [ 
31873     'action', 'background', 'codebase', 'dynsrc', 'href', 'lowsrc' 
31874 ];
31875
31876 // protocols..
31877 Roo.HtmlEditorCore.pwhite= [
31878         'http',  'https',  'mailto'
31879 ];
31880
31881 // white listed style attributes.
31882 Roo.HtmlEditorCore.cwhite= [
31883       //  'text-align', /// default is to allow most things..
31884       
31885          
31886 //        'font-size'//??
31887 ];
31888
31889 // black listed style attributes.
31890 Roo.HtmlEditorCore.cblack= [
31891       //  'font-size' -- this can be set by the project 
31892 ];
31893
31894
31895
31896
31897     /*
31898  * - LGPL
31899  *
31900  * HtmlEditor
31901  * 
31902  */
31903
31904 /**
31905  * @class Roo.bootstrap.form.HtmlEditor
31906  * @extends Roo.bootstrap.form.TextArea
31907  * Bootstrap HtmlEditor class
31908
31909  * @constructor
31910  * Create a new HtmlEditor
31911  * @param {Object} config The config object
31912  */
31913
31914 Roo.bootstrap.form.HtmlEditor = function(config){
31915     Roo.bootstrap.form.HtmlEditor.superclass.constructor.call(this, config);
31916     if (!this.toolbars) {
31917         this.toolbars = [];
31918     }
31919     
31920     this.editorcore = new Roo.HtmlEditorCore(Roo.apply({ owner : this} , config));
31921     this.addEvents({
31922             /**
31923              * @event initialize
31924              * Fires when the editor is fully initialized (including the iframe)
31925              * @param {HtmlEditor} this
31926              */
31927             initialize: true,
31928             /**
31929              * @event activate
31930              * Fires when the editor is first receives the focus. Any insertion must wait
31931              * until after this event.
31932              * @param {HtmlEditor} this
31933              */
31934             activate: true,
31935              /**
31936              * @event beforesync
31937              * Fires before the textarea is updated with content from the editor iframe. Return false
31938              * to cancel the sync.
31939              * @param {HtmlEditor} this
31940              * @param {String} html
31941              */
31942             beforesync: true,
31943              /**
31944              * @event beforepush
31945              * Fires before the iframe editor is updated with content from the textarea. Return false
31946              * to cancel the push.
31947              * @param {HtmlEditor} this
31948              * @param {String} html
31949              */
31950             beforepush: true,
31951              /**
31952              * @event sync
31953              * Fires when the textarea is updated with content from the editor iframe.
31954              * @param {HtmlEditor} this
31955              * @param {String} html
31956              */
31957             sync: true,
31958              /**
31959              * @event push
31960              * Fires when the iframe editor is updated with content from the textarea.
31961              * @param {HtmlEditor} this
31962              * @param {String} html
31963              */
31964             push: true,
31965              /**
31966              * @event editmodechange
31967              * Fires when the editor switches edit modes
31968              * @param {HtmlEditor} this
31969              * @param {Boolean} sourceEdit True if source edit, false if standard editing.
31970              */
31971             editmodechange: true,
31972             /**
31973              * @event editorevent
31974              * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
31975              * @param {HtmlEditor} this
31976              */
31977             editorevent: true,
31978             /**
31979              * @event firstfocus
31980              * Fires when on first focus - needed by toolbars..
31981              * @param {HtmlEditor} this
31982              */
31983             firstfocus: true,
31984             /**
31985              * @event autosave
31986              * Auto save the htmlEditor value as a file into Events
31987              * @param {HtmlEditor} this
31988              */
31989             autosave: true,
31990             /**
31991              * @event savedpreview
31992              * preview the saved version of htmlEditor
31993              * @param {HtmlEditor} this
31994              */
31995             savedpreview: true
31996         });
31997 };
31998
31999
32000 Roo.extend(Roo.bootstrap.form.HtmlEditor, Roo.bootstrap.form.TextArea,  {
32001     
32002     
32003       /**
32004      * @cfg {Array} toolbars Array of toolbars. - defaults to just the Standard one
32005      */
32006     toolbars : false,
32007     
32008      /**
32009     * @cfg {Array} buttons Array of toolbar's buttons. - defaults to empty
32010     */
32011     btns : [],
32012    
32013      /**
32014      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
32015      *                        Roo.resizable.
32016      */
32017     resizable : false,
32018      /**
32019      * @cfg {Number} height (in pixels)
32020      */   
32021     height: 300,
32022    /**
32023      * @cfg {Number} width (in pixels)
32024      */   
32025     width: false,
32026     
32027     /**
32028      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
32029      * 
32030      */
32031     stylesheets: false,
32032     
32033     // id of frame..
32034     frameId: false,
32035     
32036     // private properties
32037     validationEvent : false,
32038     deferHeight: true,
32039     initialized : false,
32040     activated : false,
32041     
32042     onFocus : Roo.emptyFn,
32043     iframePad:3,
32044     hideMode:'offsets',
32045     
32046     tbContainer : false,
32047     
32048     bodyCls : '',
32049     
32050     toolbarContainer :function() {
32051         return this.wrap.select('.x-html-editor-tb',true).first();
32052     },
32053
32054     /**
32055      * Protected method that will not generally be called directly. It
32056      * is called when the editor creates its toolbar. Override this method if you need to
32057      * add custom toolbar buttons.
32058      * @param {HtmlEditor} editor
32059      */
32060     createToolbar : function(){
32061         Roo.log('renewing');
32062         Roo.log("create toolbars");
32063         
32064         this.toolbars = [ new Roo.bootstrap.form.HtmlEditorToolbarStandard({editor: this} ) ];
32065         this.toolbars[0].render(this.toolbarContainer());
32066         
32067         return;
32068         
32069 //        if (!editor.toolbars || !editor.toolbars.length) {
32070 //            editor.toolbars = [ new Roo.bootstrap.form.HtmlEditorToolbarStandard() ]; // can be empty?
32071 //        }
32072 //        
32073 //        for (var i =0 ; i < editor.toolbars.length;i++) {
32074 //            editor.toolbars[i] = Roo.factory(
32075 //                    typeof(editor.toolbars[i]) == 'string' ?
32076 //                        { xtype: editor.toolbars[i]} : editor.toolbars[i],
32077 //                Roo.bootstrap.form.HtmlEditor);
32078 //            editor.toolbars[i].init(editor);
32079 //        }
32080     },
32081
32082      
32083     // private
32084     onRender : function(ct, position)
32085     {
32086        // Roo.log("Call onRender: " + this.xtype);
32087         var _t = this;
32088         Roo.bootstrap.form.HtmlEditor.superclass.onRender.call(this, ct, position);
32089       
32090         this.wrap = this.inputEl().wrap({
32091             cls:'x-html-editor-wrap', cn:{cls:'x-html-editor-tb'}
32092         });
32093         
32094         this.editorcore.onRender(ct, position);
32095          
32096         if (this.resizable) {
32097             this.resizeEl = new Roo.Resizable(this.wrap, {
32098                 pinned : true,
32099                 wrap: true,
32100                 dynamic : true,
32101                 minHeight : this.height,
32102                 height: this.height,
32103                 handles : this.resizable,
32104                 width: this.width,
32105                 listeners : {
32106                     resize : function(r, w, h) {
32107                         _t.onResize(w,h); // -something
32108                     }
32109                 }
32110             });
32111             
32112         }
32113         this.createToolbar(this);
32114        
32115         
32116         if(!this.width && this.resizable){
32117             this.setSize(this.wrap.getSize());
32118         }
32119         if (this.resizeEl) {
32120             this.resizeEl.resizeTo.defer(100, this.resizeEl,[ this.width,this.height ] );
32121             // should trigger onReize..
32122         }
32123         
32124     },
32125
32126     // private
32127     onResize : function(w, h)
32128     {
32129         Roo.log('resize: ' +w + ',' + h );
32130         Roo.bootstrap.form.HtmlEditor.superclass.onResize.apply(this, arguments);
32131         var ew = false;
32132         var eh = false;
32133         
32134         if(this.inputEl() ){
32135             if(typeof w == 'number'){
32136                 var aw = w - this.wrap.getFrameWidth('lr');
32137                 this.inputEl().setWidth(this.adjustWidth('textarea', aw));
32138                 ew = aw;
32139             }
32140             if(typeof h == 'number'){
32141                  var tbh = -11;  // fixme it needs to tool bar size!
32142                 for (var i =0; i < this.toolbars.length;i++) {
32143                     // fixme - ask toolbars for heights?
32144                     tbh += this.toolbars[i].el.getHeight();
32145                     //if (this.toolbars[i].footer) {
32146                     //    tbh += this.toolbars[i].footer.el.getHeight();
32147                     //}
32148                 }
32149               
32150                 
32151                 
32152                 
32153                 
32154                 var ah = h - this.wrap.getFrameWidth('tb') - tbh;// this.tb.el.getHeight();
32155                 ah -= 5; // knock a few pixes off for look..
32156                 this.inputEl().setHeight(this.adjustWidth('textarea', ah));
32157                 var eh = ah;
32158             }
32159         }
32160         Roo.log('onResize:' + [w,h,ew,eh].join(',') );
32161         this.editorcore.onResize(ew,eh);
32162         
32163     },
32164
32165     /**
32166      * Toggles the editor between standard and source edit mode.
32167      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
32168      */
32169     toggleSourceEdit : function(sourceEditMode)
32170     {
32171         this.editorcore.toggleSourceEdit(sourceEditMode);
32172         
32173         if(this.editorcore.sourceEditMode){
32174             Roo.log('editor - showing textarea');
32175             
32176 //            Roo.log('in');
32177 //            Roo.log(this.syncValue());
32178             this.syncValue();
32179             this.inputEl().removeClass(['hide', 'x-hidden']);
32180             this.inputEl().dom.removeAttribute('tabIndex');
32181             this.inputEl().focus();
32182         }else{
32183             Roo.log('editor - hiding textarea');
32184 //            Roo.log('out')
32185 //            Roo.log(this.pushValue()); 
32186             this.pushValue();
32187             
32188             this.inputEl().addClass(['hide', 'x-hidden']);
32189             this.inputEl().dom.setAttribute('tabIndex', -1);
32190             //this.deferFocus();
32191         }
32192          
32193         if(this.resizable){
32194             this.setSize(this.wrap.getSize());
32195         }
32196         
32197         this.fireEvent('editmodechange', this, this.editorcore.sourceEditMode);
32198     },
32199  
32200     // private (for BoxComponent)
32201     adjustSize : Roo.BoxComponent.prototype.adjustSize,
32202
32203     // private (for BoxComponent)
32204     getResizeEl : function(){
32205         return this.wrap;
32206     },
32207
32208     // private (for BoxComponent)
32209     getPositionEl : function(){
32210         return this.wrap;
32211     },
32212
32213     // private
32214     initEvents : function(){
32215         this.originalValue = this.getValue();
32216     },
32217
32218 //    /**
32219 //     * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
32220 //     * @method
32221 //     */
32222 //    markInvalid : Roo.emptyFn,
32223 //    /**
32224 //     * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
32225 //     * @method
32226 //     */
32227 //    clearInvalid : Roo.emptyFn,
32228
32229     setValue : function(v){
32230         Roo.bootstrap.form.HtmlEditor.superclass.setValue.call(this, v);
32231         this.editorcore.pushValue();
32232     },
32233
32234      
32235     // private
32236     deferFocus : function(){
32237         this.focus.defer(10, this);
32238     },
32239
32240     // doc'ed in Field
32241     focus : function(){
32242         this.editorcore.focus();
32243         
32244     },
32245       
32246
32247     // private
32248     onDestroy : function(){
32249         
32250         
32251         
32252         if(this.rendered){
32253             
32254             for (var i =0; i < this.toolbars.length;i++) {
32255                 // fixme - ask toolbars for heights?
32256                 this.toolbars[i].onDestroy();
32257             }
32258             
32259             this.wrap.dom.innerHTML = '';
32260             this.wrap.remove();
32261         }
32262     },
32263
32264     // private
32265     onFirstFocus : function(){
32266         //Roo.log("onFirstFocus");
32267         this.editorcore.onFirstFocus();
32268          for (var i =0; i < this.toolbars.length;i++) {
32269             this.toolbars[i].onFirstFocus();
32270         }
32271         
32272     },
32273     
32274     // private
32275     syncValue : function()
32276     {   
32277         this.editorcore.syncValue();
32278     },
32279     
32280     pushValue : function()
32281     {   
32282         this.editorcore.pushValue();
32283     }
32284      
32285     
32286     // hide stuff that is not compatible
32287     /**
32288      * @event blur
32289      * @hide
32290      */
32291     /**
32292      * @event change
32293      * @hide
32294      */
32295     /**
32296      * @event focus
32297      * @hide
32298      */
32299     /**
32300      * @event specialkey
32301      * @hide
32302      */
32303     /**
32304      * @cfg {String} fieldClass @hide
32305      */
32306     /**
32307      * @cfg {String} focusClass @hide
32308      */
32309     /**
32310      * @cfg {String} autoCreate @hide
32311      */
32312     /**
32313      * @cfg {String} inputType @hide
32314      */
32315      
32316     /**
32317      * @cfg {String} invalidText @hide
32318      */
32319     /**
32320      * @cfg {String} msgFx @hide
32321      */
32322     /**
32323      * @cfg {String} validateOnBlur @hide
32324      */
32325 });
32326  
32327     
32328    
32329    
32330    
32331       
32332 Roo.namespace('Roo.bootstrap.form.HtmlEditor');
32333 /**
32334  * @class Roo.bootstrap.form.HtmlEditorToolbarStandard
32335  * @parent Roo.bootstrap.form.HtmlEditor
32336  * @extends Roo.bootstrap.nav.Simplebar
32337  * Basic Toolbar
32338  * 
32339  * @example
32340  * Usage:
32341  *
32342  new Roo.bootstrap.form.HtmlEditor({
32343     ....
32344     toolbars : [
32345         new Roo.bootstrap.form.HtmlEditorToolbarStandard({
32346             disable : { fonts: 1 , format: 1, ..., ... , ...],
32347             btns : [ .... ]
32348         })
32349     }
32350      
32351  * 
32352  * @cfg {Object} disable List of elements to disable..
32353  * @cfg {Array} btns List of additional buttons.
32354  * 
32355  * 
32356  * NEEDS Extra CSS? 
32357  * .x-html-editor-tb .x-edit-none .x-btn-text { background: none; }
32358  */
32359  
32360 Roo.bootstrap.form.HtmlEditorToolbarStandard = function(config)
32361 {
32362     
32363     Roo.apply(this, config);
32364     
32365     // default disabled, based on 'good practice'..
32366     this.disable = this.disable || {};
32367     Roo.applyIf(this.disable, {
32368         fontSize : true,
32369         colors : true,
32370         specialElements : true
32371     });
32372     Roo.bootstrap.form.HtmlEditorToolbarStandard.superclass.constructor.call(this, config);
32373     
32374     this.editor = config.editor;
32375     this.editorcore = config.editor.editorcore;
32376     
32377     this.buttons   = new Roo.util.MixedCollection(false, function(o) { return o.cmd; });
32378     
32379     //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
32380     // dont call parent... till later.
32381 }
32382 Roo.extend(Roo.bootstrap.form.HtmlEditorToolbarStandard, Roo.bootstrap.nav.Simplebar,  {
32383      
32384     bar : true,
32385     
32386     editor : false,
32387     editorcore : false,
32388     
32389     
32390     formats : [
32391         "p" ,  
32392         "h1","h2","h3","h4","h5","h6", 
32393         "pre", "code", 
32394         "abbr", "acronym", "address", "cite", "samp", "var",
32395         'div','span'
32396     ],
32397     
32398     onRender : function(ct, position)
32399     {
32400        // Roo.log("Call onRender: " + this.xtype);
32401         
32402        Roo.bootstrap.form.HtmlEditorToolbarStandard.superclass.onRender.call(this, ct, position);
32403        Roo.log(this.el);
32404        this.el.dom.style.marginBottom = '0';
32405        var _this = this;
32406        var editorcore = this.editorcore;
32407        var editor= this.editor;
32408        
32409        var children = [];
32410        var btn = function(id,cmd , toggle, handler, html){
32411        
32412             var  event = toggle ? 'toggle' : 'click';
32413        
32414             var a = {
32415                 size : 'sm',
32416                 xtype: 'Button',
32417                 xns: Roo.bootstrap,
32418                 //glyphicon : id,
32419                 fa: id,
32420                 cmd : id || cmd,
32421                 enableToggle:toggle !== false,
32422                 html : html || '',
32423                 pressed : toggle ? false : null,
32424                 listeners : {}
32425             };
32426             a.listeners[toggle ? 'toggle' : 'click'] = function() {
32427                 handler ? handler.call(_this,this) :_this.onBtnClick.call(_this, cmd ||  id);
32428             };
32429             children.push(a);
32430             return a;
32431        }
32432        
32433     //    var cb_box = function...
32434         
32435         var style = {
32436                 xtype: 'Button',
32437                 size : 'sm',
32438                 xns: Roo.bootstrap,
32439                 fa : 'font',
32440                 //html : 'submit'
32441                 menu : {
32442                     xtype: 'Menu',
32443                     xns: Roo.bootstrap,
32444                     items:  []
32445                 }
32446         };
32447         Roo.each(this.formats, function(f) {
32448             style.menu.items.push({
32449                 xtype :'MenuItem',
32450                 xns: Roo.bootstrap,
32451                 html : '<'+ f+' style="margin:2px">'+f +'</'+ f+'>',
32452                 tagname : f,
32453                 listeners : {
32454                     click : function()
32455                     {
32456                         editorcore.insertTag(this.tagname);
32457                         editor.focus();
32458                     }
32459                 }
32460                 
32461             });
32462         });
32463         children.push(style);   
32464         
32465         btn('bold',false,true);
32466         btn('italic',false,true);
32467         btn('align-left', 'justifyleft',true);
32468         btn('align-center', 'justifycenter',true);
32469         btn('align-right' , 'justifyright',true);
32470         btn('link', false, false, function(btn) {
32471             //Roo.log("create link?");
32472             var url = prompt(this.createLinkText, this.defaultLinkValue);
32473             if(url && url != 'http:/'+'/'){
32474                 this.editorcore.relayCmd('createlink', url);
32475             }
32476         }),
32477         btn('list','insertunorderedlist',true);
32478         btn('pencil', false,true, function(btn){
32479                 Roo.log(this);
32480                 this.toggleSourceEdit(btn.pressed);
32481         });
32482         
32483         if (this.editor.btns.length > 0) {
32484             for (var i = 0; i<this.editor.btns.length; i++) {
32485                 children.push(this.editor.btns[i]);
32486             }
32487         }
32488         
32489         /*
32490         var cog = {
32491                 xtype: 'Button',
32492                 size : 'sm',
32493                 xns: Roo.bootstrap,
32494                 glyphicon : 'cog',
32495                 //html : 'submit'
32496                 menu : {
32497                     xtype: 'Menu',
32498                     xns: Roo.bootstrap,
32499                     items:  []
32500                 }
32501         };
32502         
32503         cog.menu.items.push({
32504             xtype :'MenuItem',
32505             xns: Roo.bootstrap,
32506             html : Clean styles,
32507             tagname : f,
32508             listeners : {
32509                 click : function()
32510                 {
32511                     editorcore.insertTag(this.tagname);
32512                     editor.focus();
32513                 }
32514             }
32515             
32516         });
32517        */
32518         
32519          
32520        this.xtype = 'NavSimplebar';
32521         
32522         for(var i=0;i< children.length;i++) {
32523             
32524             this.buttons.add(this.addxtypeChild(children[i]));
32525             
32526         }
32527         
32528         editor.on('editorevent', this.updateToolbar, this);
32529     },
32530     onBtnClick : function(id)
32531     {
32532        this.editorcore.relayCmd(id);
32533        this.editorcore.focus();
32534     },
32535     
32536     /**
32537      * Protected method that will not generally be called directly. It triggers
32538      * a toolbar update by reading the markup state of the current selection in the editor.
32539      */
32540     updateToolbar: function(){
32541
32542         if(!this.editorcore.activated){
32543             this.editor.onFirstFocus(); // is this neeed?
32544             return;
32545         }
32546
32547         var btns = this.buttons; 
32548         var doc = this.editorcore.doc;
32549         btns.get('bold').setActive(doc.queryCommandState('bold'));
32550         btns.get('italic').setActive(doc.queryCommandState('italic'));
32551         //btns.get('underline').setActive(doc.queryCommandState('underline'));
32552         
32553         btns.get('align-left').setActive(doc.queryCommandState('justifyleft'));
32554         btns.get('align-center').setActive(doc.queryCommandState('justifycenter'));
32555         btns.get('align-right').setActive(doc.queryCommandState('justifyright'));
32556         
32557         //btns[frameId + '-insertorderedlist').setActive(doc.queryCommandState('insertorderedlist'));
32558         btns.get('list').setActive(doc.queryCommandState('insertunorderedlist'));
32559          /*
32560         
32561         var ans = this.editorcore.getAllAncestors();
32562         if (this.formatCombo) {
32563             
32564             
32565             var store = this.formatCombo.store;
32566             this.formatCombo.setValue("");
32567             for (var i =0; i < ans.length;i++) {
32568                 if (ans[i] && store.query('tag',ans[i].tagName.toLowerCase(), false).length) {
32569                     // select it..
32570                     this.formatCombo.setValue(ans[i].tagName.toLowerCase());
32571                     break;
32572                 }
32573             }
32574         }
32575         
32576         
32577         
32578         // hides menus... - so this cant be on a menu...
32579         Roo.bootstrap.MenuMgr.hideAll();
32580         */
32581         Roo.bootstrap.menu.Manager.hideAll();
32582         //this.editorsyncValue();
32583     },
32584     onFirstFocus: function() {
32585         this.buttons.each(function(item){
32586            item.enable();
32587         });
32588     },
32589     toggleSourceEdit : function(sourceEditMode){
32590         
32591           
32592         if(sourceEditMode){
32593             Roo.log("disabling buttons");
32594            this.buttons.each( function(item){
32595                 if(item.cmd != 'pencil'){
32596                     item.disable();
32597                 }
32598             });
32599           
32600         }else{
32601             Roo.log("enabling buttons");
32602             if(this.editorcore.initialized){
32603                 this.buttons.each( function(item){
32604                     item.enable();
32605                 });
32606             }
32607             
32608         }
32609         Roo.log("calling toggole on editor");
32610         // tell the editor that it's been pressed..
32611         this.editor.toggleSourceEdit(sourceEditMode);
32612        
32613     }
32614 });
32615
32616
32617
32618
32619  
32620 /*
32621  * - LGPL
32622  */
32623
32624 /**
32625  * @class Roo.bootstrap.form.Markdown
32626  * @extends Roo.bootstrap.form.TextArea
32627  * Bootstrap Showdown editable area
32628  * @cfg {string} content
32629  * 
32630  * @constructor
32631  * Create a new Showdown
32632  */
32633
32634 Roo.bootstrap.form.Markdown = function(config){
32635     Roo.bootstrap.form.Markdown.superclass.constructor.call(this, config);
32636    
32637 };
32638
32639 Roo.extend(Roo.bootstrap.form.Markdown, Roo.bootstrap.form.TextArea,  {
32640     
32641     editing :false,
32642     
32643     initEvents : function()
32644     {
32645         
32646         Roo.bootstrap.form.TextArea.prototype.initEvents.call(this);
32647         this.markdownEl = this.el.createChild({
32648             cls : 'roo-markdown-area'
32649         });
32650         this.inputEl().addClass('d-none');
32651         if (this.getValue() == '') {
32652             this.markdownEl.dom.innerHTML = String.format('<span class="roo-placeholder">{0}</span>', this.placeholder || '');
32653             
32654         } else {
32655             this.markdownEl.dom.innerHTML = Roo.Markdown.toHtml(Roo.util.Format.htmlEncode(this.getValue()));
32656         }
32657         this.markdownEl.on('click', this.toggleTextEdit, this);
32658         this.on('blur', this.toggleTextEdit, this);
32659         this.on('specialkey', this.resizeTextArea, this);
32660     },
32661     
32662     toggleTextEdit : function()
32663     {
32664         var sh = this.markdownEl.getHeight();
32665         this.inputEl().addClass('d-none');
32666         this.markdownEl.addClass('d-none');
32667         if (!this.editing) {
32668             // show editor?
32669             this.inputEl().setHeight(Math.min(500, Math.max(sh,(this.getValue().split("\n").length+1) * 30)));
32670             this.inputEl().removeClass('d-none');
32671             this.inputEl().focus();
32672             this.editing = true;
32673             return;
32674         }
32675         // show showdown...
32676         this.updateMarkdown();
32677         this.markdownEl.removeClass('d-none');
32678         this.editing = false;
32679         return;
32680     },
32681     updateMarkdown : function()
32682     {
32683         if (this.getValue() == '') {
32684             this.markdownEl.dom.innerHTML = String.format('<span class="roo-placeholder">{0}</span>', this.placeholder || '');
32685             return;
32686         }
32687  
32688         this.markdownEl.dom.innerHTML = Roo.Markdown.toHtml(Roo.util.Format.htmlEncode(this.getValue()));
32689     },
32690     
32691     resizeTextArea: function () {
32692         
32693         var sh = 100;
32694         Roo.log([sh, this.getValue().split("\n").length * 30]);
32695         this.inputEl().setHeight(Math.min(500, Math.max(sh, (this.getValue().split("\n").length +1) * 30)));
32696     },
32697     setValue : function(val)
32698     {
32699         Roo.bootstrap.form.TextArea.prototype.setValue.call(this,val);
32700         if (!this.editing) {
32701             this.updateMarkdown();
32702         }
32703         
32704     },
32705     focus : function()
32706     {
32707         if (!this.editing) {
32708             this.toggleTextEdit();
32709         }
32710         
32711     }
32712
32713
32714 });/*
32715  * Based on:
32716  * Ext JS Library 1.1.1
32717  * Copyright(c) 2006-2007, Ext JS, LLC.
32718  *
32719  * Originally Released Under LGPL - original licence link has changed is not relivant.
32720  *
32721  * Fork - LGPL
32722  * <script type="text/javascript">
32723  */
32724  
32725 /**
32726  * @class Roo.bootstrap.PagingToolbar
32727  * @extends Roo.bootstrap.nav.Simplebar
32728  * A specialized toolbar that is bound to a {@link Roo.data.Store} and provides automatic paging controls.
32729  * @constructor
32730  * Create a new PagingToolbar
32731  * @param {Object} config The config object
32732  * @param {Roo.data.Store} store
32733  */
32734 Roo.bootstrap.PagingToolbar = function(config)
32735 {
32736     // old args format still supported... - xtype is prefered..
32737         // created from xtype...
32738     
32739     this.ds = config.dataSource;
32740     
32741     if (config.store && !this.ds) {
32742         this.store= Roo.factory(config.store, Roo.data);
32743         this.ds = this.store;
32744         this.ds.xmodule = this.xmodule || false;
32745     }
32746     
32747     this.toolbarItems = [];
32748     if (config.items) {
32749         this.toolbarItems = config.items;
32750     }
32751     
32752     Roo.bootstrap.PagingToolbar.superclass.constructor.call(this, config);
32753     
32754     this.cursor = 0;
32755     
32756     if (this.ds) { 
32757         this.bind(this.ds);
32758     }
32759     
32760     if (Roo.bootstrap.version == 4) {
32761         this.navgroup = new Roo.bootstrap.ButtonGroup({ cls: 'pagination' });
32762     } else {
32763         this.navgroup = new Roo.bootstrap.nav.Group({ cls: 'pagination' });
32764     }
32765     
32766 };
32767
32768 Roo.extend(Roo.bootstrap.PagingToolbar, Roo.bootstrap.nav.Simplebar, {
32769     /**
32770      * @cfg {Roo.bootstrap.Button} buttons[]
32771      * Buttons for the toolbar
32772      */
32773      /**
32774      * @cfg {Roo.data.Store} store
32775      * The underlying data store providing the paged data
32776      */
32777     /**
32778      * @cfg {String/HTMLElement/Element} container
32779      * container The id or element that will contain the toolbar
32780      */
32781     /**
32782      * @cfg {Boolean} displayInfo
32783      * True to display the displayMsg (defaults to false)
32784      */
32785     /**
32786      * @cfg {Number} pageSize
32787      * The number of records to display per page (defaults to 20)
32788      */
32789     pageSize: 20,
32790     /**
32791      * @cfg {String} displayMsg
32792      * The paging status message to display (defaults to "Displaying {start} - {end} of {total}")
32793      */
32794     displayMsg : 'Displaying {0} - {1} of {2}',
32795     /**
32796      * @cfg {String} emptyMsg
32797      * The message to display when no records are found (defaults to "No data to display")
32798      */
32799     emptyMsg : 'No data to display',
32800     /**
32801      * Customizable piece of the default paging text (defaults to "Page")
32802      * @type String
32803      */
32804     beforePageText : "Page",
32805     /**
32806      * Customizable piece of the default paging text (defaults to "of %0")
32807      * @type String
32808      */
32809     afterPageText : "of {0}",
32810     /**
32811      * Customizable piece of the default paging text (defaults to "First Page")
32812      * @type String
32813      */
32814     firstText : "First Page",
32815     /**
32816      * Customizable piece of the default paging text (defaults to "Previous Page")
32817      * @type String
32818      */
32819     prevText : "Previous Page",
32820     /**
32821      * Customizable piece of the default paging text (defaults to "Next Page")
32822      * @type String
32823      */
32824     nextText : "Next Page",
32825     /**
32826      * Customizable piece of the default paging text (defaults to "Last Page")
32827      * @type String
32828      */
32829     lastText : "Last Page",
32830     /**
32831      * Customizable piece of the default paging text (defaults to "Refresh")
32832      * @type String
32833      */
32834     refreshText : "Refresh",
32835
32836     buttons : false,
32837     // private
32838     onRender : function(ct, position) 
32839     {
32840         Roo.bootstrap.PagingToolbar.superclass.onRender.call(this, ct, position);
32841         this.navgroup.parentId = this.id;
32842         this.navgroup.onRender(this.el, null);
32843         // add the buttons to the navgroup
32844         
32845         if(this.displayInfo){
32846             this.el.select('ul.navbar-nav',true).first().createChild({cls:'x-paging-info'});
32847             this.displayEl = this.el.select('.x-paging-info', true).first();
32848 //            var navel = this.navgroup.addItem( { tagtype : 'span', html : '', cls : 'x-paging-info', preventDefault : true } );
32849 //            this.displayEl = navel.el.select('span',true).first();
32850         }
32851         
32852         var _this = this;
32853         
32854         if(this.buttons){
32855             Roo.each(_this.buttons, function(e){ // this might need to use render????
32856                Roo.factory(e).render(_this.el);
32857             });
32858         }
32859             
32860         Roo.each(_this.toolbarItems, function(e) {
32861             _this.navgroup.addItem(e);
32862         });
32863         
32864         
32865         this.first = this.navgroup.addItem({
32866             tooltip: this.firstText,
32867             cls: "prev btn-outline-secondary",
32868             html : ' <i class="fa fa-step-backward"></i>',
32869             disabled: true,
32870             preventDefault: true,
32871             listeners : { click : this.onClick.createDelegate(this, ["first"]) }
32872         });
32873         
32874         this.prev =  this.navgroup.addItem({
32875             tooltip: this.prevText,
32876             cls: "prev btn-outline-secondary",
32877             html : ' <i class="fa fa-backward"></i>',
32878             disabled: true,
32879             preventDefault: true,
32880             listeners : { click :  this.onClick.createDelegate(this, ["prev"]) }
32881         });
32882     //this.addSeparator();
32883         
32884         
32885         var field = this.navgroup.addItem( {
32886             tagtype : 'span',
32887             cls : 'x-paging-position  btn-outline-secondary',
32888              disabled: true,
32889             html : this.beforePageText  +
32890                 '<input type="text" size="3" value="1" class="x-grid-page-number">' +
32891                 '<span class="x-paging-after">' +  String.format(this.afterPageText, 1) + '</span>'
32892          } ); //?? escaped?
32893         
32894         this.field = field.el.select('input', true).first();
32895         this.field.on("keydown", this.onPagingKeydown, this);
32896         this.field.on("focus", function(){this.dom.select();});
32897     
32898     
32899         this.afterTextEl =  field.el.select('.x-paging-after',true).first();
32900         //this.field.setHeight(18);
32901         //this.addSeparator();
32902         this.next = this.navgroup.addItem({
32903             tooltip: this.nextText,
32904             cls: "next btn-outline-secondary",
32905             html : ' <i class="fa fa-forward"></i>',
32906             disabled: true,
32907             preventDefault: true,
32908             listeners : { click :  this.onClick.createDelegate(this, ["next"]) }
32909         });
32910         this.last = this.navgroup.addItem({
32911             tooltip: this.lastText,
32912             html : ' <i class="fa fa-step-forward"></i>',
32913             cls: "next btn-outline-secondary",
32914             disabled: true,
32915             preventDefault: true,
32916             listeners : { click :  this.onClick.createDelegate(this, ["last"]) }
32917         });
32918     //this.addSeparator();
32919         this.loading = this.navgroup.addItem({
32920             tooltip: this.refreshText,
32921             cls: "btn-outline-secondary",
32922             html : ' <i class="fa fa-refresh"></i>',
32923             preventDefault: true,
32924             listeners : { click : this.onClick.createDelegate(this, ["refresh"]) }
32925         });
32926         
32927     },
32928
32929     // private
32930     updateInfo : function(){
32931         if(this.displayEl){
32932             var count = (typeof(this.getCount) == 'undefined') ? this.ds.getCount() : this.getCount();
32933             var msg = count == 0 ?
32934                 this.emptyMsg :
32935                 String.format(
32936                     this.displayMsg,
32937                     this.cursor+1, this.cursor+count, this.ds.getTotalCount()    
32938                 );
32939             this.displayEl.update(msg);
32940         }
32941     },
32942
32943     // private
32944     onLoad : function(ds, r, o)
32945     {
32946         this.cursor = o.params && o.params.start ? o.params.start : 0;
32947         
32948         var d = this.getPageData(),
32949             ap = d.activePage,
32950             ps = d.pages;
32951         
32952         
32953         this.afterTextEl.dom.innerHTML = String.format(this.afterPageText, d.pages);
32954         this.field.dom.value = ap;
32955         this.first.setDisabled(ap == 1);
32956         this.prev.setDisabled(ap == 1);
32957         this.next.setDisabled(ap == ps);
32958         this.last.setDisabled(ap == ps);
32959         this.loading.enable();
32960         this.updateInfo();
32961     },
32962
32963     // private
32964     getPageData : function(){
32965         var total = this.ds.getTotalCount();
32966         return {
32967             total : total,
32968             activePage : Math.ceil((this.cursor+this.pageSize)/this.pageSize),
32969             pages :  total < this.pageSize ? 1 : Math.ceil(total/this.pageSize)
32970         };
32971     },
32972
32973     // private
32974     onLoadError : function(proxy, o){
32975         this.loading.enable();
32976         if (this.ds.events.loadexception.listeners.length  < 2) {
32977             // nothing has been assigned to loadexception except this...
32978             // so 
32979             Roo.MessageBox.alert("Error loading",o.raw.errorMsg);
32980
32981         }
32982     },
32983
32984     // private
32985     onPagingKeydown : function(e){
32986         var k = e.getKey();
32987         var d = this.getPageData();
32988         if(k == e.RETURN){
32989             var v = this.field.dom.value, pageNum;
32990             if(!v || isNaN(pageNum = parseInt(v, 10))){
32991                 this.field.dom.value = d.activePage;
32992                 return;
32993             }
32994             pageNum = Math.min(Math.max(1, pageNum), d.pages) - 1;
32995             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
32996             e.stopEvent();
32997         }
32998         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))
32999         {
33000           var pageNum = (k == e.HOME || (k == e.DOWN && e.ctrlKey) || (k == e.LEFT && e.ctrlKey) || (k == e.PAGEDOWN && e.ctrlKey)) ? 1 : d.pages;
33001           this.field.dom.value = pageNum;
33002           this.ds.load({params:{start: (pageNum - 1) * this.pageSize, limit: this.pageSize}});
33003           e.stopEvent();
33004         }
33005         else if(k == e.UP || k == e.RIGHT || k == e.PAGEUP || k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
33006         {
33007           var v = this.field.dom.value, pageNum; 
33008           var increment = (e.shiftKey) ? 10 : 1;
33009           if(k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN) {
33010                 increment *= -1;
33011           }
33012           if(!v || isNaN(pageNum = parseInt(v, 10))) {
33013             this.field.dom.value = d.activePage;
33014             return;
33015           }
33016           else if(parseInt(v, 10) + increment >= 1 & parseInt(v, 10) + increment <= d.pages)
33017           {
33018             this.field.dom.value = parseInt(v, 10) + increment;
33019             pageNum = Math.min(Math.max(1, pageNum + increment), d.pages) - 1;
33020             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
33021           }
33022           e.stopEvent();
33023         }
33024     },
33025
33026     // private
33027     beforeLoad : function(){
33028         if(this.loading){
33029             this.loading.disable();
33030         }
33031     },
33032
33033     // private
33034     onClick : function(which){
33035         
33036         var ds = this.ds;
33037         if (!ds) {
33038             return;
33039         }
33040         
33041         switch(which){
33042             case "first":
33043                 ds.load({params:{start: 0, limit: this.pageSize}});
33044             break;
33045             case "prev":
33046                 ds.load({params:{start: Math.max(0, this.cursor-this.pageSize), limit: this.pageSize}});
33047             break;
33048             case "next":
33049                 ds.load({params:{start: this.cursor+this.pageSize, limit: this.pageSize}});
33050             break;
33051             case "last":
33052                 var total = ds.getTotalCount();
33053                 var extra = total % this.pageSize;
33054                 var lastStart = extra ? (total - extra) : total-this.pageSize;
33055                 ds.load({params:{start: lastStart, limit: this.pageSize}});
33056             break;
33057             case "refresh":
33058                 ds.load({params:{start: this.cursor, limit: this.pageSize}});
33059             break;
33060         }
33061     },
33062
33063     /**
33064      * Unbinds the paging toolbar from the specified {@link Roo.data.Store}
33065      * @param {Roo.data.Store} store The data store to unbind
33066      */
33067     unbind : function(ds){
33068         ds.un("beforeload", this.beforeLoad, this);
33069         ds.un("load", this.onLoad, this);
33070         ds.un("loadexception", this.onLoadError, this);
33071         ds.un("remove", this.updateInfo, this);
33072         ds.un("add", this.updateInfo, this);
33073         this.ds = undefined;
33074     },
33075
33076     /**
33077      * Binds the paging toolbar to the specified {@link Roo.data.Store}
33078      * @param {Roo.data.Store} store The data store to bind
33079      */
33080     bind : function(ds){
33081         ds.on("beforeload", this.beforeLoad, this);
33082         ds.on("load", this.onLoad, this);
33083         ds.on("loadexception", this.onLoadError, this);
33084         ds.on("remove", this.updateInfo, this);
33085         ds.on("add", this.updateInfo, this);
33086         this.ds = ds;
33087     }
33088 });/*
33089  * - LGPL
33090  *
33091  * element
33092  * 
33093  */
33094
33095 /**
33096  * @class Roo.bootstrap.MessageBar
33097  * @extends Roo.bootstrap.Component
33098  * Bootstrap MessageBar class
33099  * @cfg {String} html contents of the MessageBar
33100  * @cfg {String} weight (info | success | warning | danger) default info
33101  * @cfg {String} beforeClass insert the bar before the given class
33102  * @cfg {Boolean} closable (true | false) default false
33103  * @cfg {Boolean} fixed (true | false) default false, fix the bar at the top
33104  * 
33105  * @constructor
33106  * Create a new Element
33107  * @param {Object} config The config object
33108  */
33109
33110 Roo.bootstrap.MessageBar = function(config){
33111     Roo.bootstrap.MessageBar.superclass.constructor.call(this, config);
33112 };
33113
33114 Roo.extend(Roo.bootstrap.MessageBar, Roo.bootstrap.Component,  {
33115     
33116     html: '',
33117     weight: 'info',
33118     closable: false,
33119     fixed: false,
33120     beforeClass: 'bootstrap-sticky-wrap',
33121     
33122     getAutoCreate : function(){
33123         
33124         var cfg = {
33125             tag: 'div',
33126             cls: 'alert alert-dismissable alert-' + this.weight,
33127             cn: [
33128                 {
33129                     tag: 'span',
33130                     cls: 'message',
33131                     html: this.html || ''
33132                 }
33133             ]
33134         };
33135         
33136         if(this.fixed){
33137             cfg.cls += ' alert-messages-fixed';
33138         }
33139         
33140         if(this.closable){
33141             cfg.cn.push({
33142                 tag: 'button',
33143                 cls: 'close',
33144                 html: 'x'
33145             });
33146         }
33147         
33148         return cfg;
33149     },
33150     
33151     onRender : function(ct, position)
33152     {
33153         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
33154         
33155         if(!this.el){
33156             var cfg = Roo.apply({},  this.getAutoCreate());
33157             cfg.id = Roo.id();
33158             
33159             if (this.cls) {
33160                 cfg.cls += ' ' + this.cls;
33161             }
33162             if (this.style) {
33163                 cfg.style = this.style;
33164             }
33165             this.el = Roo.get(document.body).createChild(cfg, Roo.select('.'+this.beforeClass, true).first());
33166             
33167             this.el.setVisibilityMode(Roo.Element.DISPLAY);
33168         }
33169         
33170         this.el.select('>button.close').on('click', this.hide, this);
33171         
33172     },
33173     
33174     show : function()
33175     {
33176         if (!this.rendered) {
33177             this.render();
33178         }
33179         
33180         this.el.show();
33181         
33182         this.fireEvent('show', this);
33183         
33184     },
33185     
33186     hide : function()
33187     {
33188         if (!this.rendered) {
33189             this.render();
33190         }
33191         
33192         this.el.hide();
33193         
33194         this.fireEvent('hide', this);
33195     },
33196     
33197     update : function()
33198     {
33199 //        var e = this.el.dom.firstChild;
33200 //        
33201 //        if(this.closable){
33202 //            e = e.nextSibling;
33203 //        }
33204 //        
33205 //        e.data = this.html || '';
33206
33207         this.el.select('>.message', true).first().dom.innerHTML = this.html || '';
33208     }
33209    
33210 });
33211
33212  
33213
33214      /*
33215  * - LGPL
33216  *
33217  * Graph
33218  * 
33219  */
33220
33221
33222 /**
33223  * @class Roo.bootstrap.Graph
33224  * @extends Roo.bootstrap.Component
33225  * Bootstrap Graph class
33226 > Prameters
33227  -sm {number} sm 4
33228  -md {number} md 5
33229  @cfg {String} graphtype  bar | vbar | pie
33230  @cfg {number} g_x coodinator | centre x (pie)
33231  @cfg {number} g_y coodinator | centre y (pie)
33232  @cfg {number} g_r radius (pie)
33233  @cfg {number} g_height height of the chart (respected by all elements in the set)
33234  @cfg {number} g_width width of the chart (respected by all elements in the set)
33235  @cfg {Object} title The title of the chart
33236     
33237  -{Array}  values
33238  -opts (object) options for the chart 
33239      o {
33240      o type (string) type of endings of the bar. Default: 'square'. Other options are: 'round', 'sharp', 'soft'.
33241      o gutter (number)(string) default '20%' (WHAT DOES IT DO?)
33242      o vgutter (number)
33243      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.
33244      o stacked (boolean) whether or not to tread values as in a stacked bar chart
33245      o to
33246      o stretch (boolean)
33247      o }
33248  -opts (object) options for the pie
33249      o{
33250      o cut
33251      o startAngle (number)
33252      o endAngle (number)
33253      } 
33254  *
33255  * @constructor
33256  * Create a new Input
33257  * @param {Object} config The config object
33258  */
33259
33260 Roo.bootstrap.Graph = function(config){
33261     Roo.bootstrap.Graph.superclass.constructor.call(this, config);
33262     
33263     this.addEvents({
33264         // img events
33265         /**
33266          * @event click
33267          * The img click event for the img.
33268          * @param {Roo.EventObject} e
33269          */
33270         "click" : true
33271     });
33272 };
33273
33274 Roo.extend(Roo.bootstrap.Graph, Roo.bootstrap.Component,  {
33275     
33276     sm: 4,
33277     md: 5,
33278     graphtype: 'bar',
33279     g_height: 250,
33280     g_width: 400,
33281     g_x: 50,
33282     g_y: 50,
33283     g_r: 30,
33284     opts:{
33285         //g_colors: this.colors,
33286         g_type: 'soft',
33287         g_gutter: '20%'
33288
33289     },
33290     title : false,
33291
33292     getAutoCreate : function(){
33293         
33294         var cfg = {
33295             tag: 'div',
33296             html : null
33297         };
33298         
33299         
33300         return  cfg;
33301     },
33302
33303     onRender : function(ct,position){
33304         
33305         
33306         Roo.bootstrap.Graph.superclass.onRender.call(this,ct,position);
33307         
33308         if (typeof(Raphael) == 'undefined') {
33309             Roo.bootstrap.MessageBox.alert("Error","Raphael is not availabe");
33310             return;
33311         }
33312         
33313         this.raphael = Raphael(this.el.dom);
33314         
33315                     // data1 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
33316                     // data2 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
33317                     // data3 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
33318                     // txtattr = { font: "12px 'Fontin Sans', Fontin-Sans, sans-serif" };
33319                 /*
33320                 r.text(160, 10, "Single Series Chart").attr(txtattr);
33321                 r.text(480, 10, "Multiline Series Chart").attr(txtattr);
33322                 r.text(160, 250, "Multiple Series Stacked Chart").attr(txtattr);
33323                 r.text(480, 250, 'Multiline Series Stacked Vertical Chart. Type "round"').attr(txtattr);
33324                 
33325                 r.barchart(10, 10, 300, 220, [[55, 20, 13, 32, 5, 1, 2, 10]], 0, {type: "sharp"});
33326                 r.barchart(330, 10, 300, 220, data1);
33327                 r.barchart(10, 250, 300, 220, data2, {stacked: true});
33328                 r.barchart(330, 250, 300, 220, data3, {stacked: true, type: "round"});
33329                 */
33330                 
33331                 // var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
33332                 // r.barchart(30, 30, 560, 250,  xdata, {
33333                 //    labels : [55, 20, 13, 32, 5, 1, 2, 10,5 , 10],
33334                 //     axis : "0 0 1 1",
33335                 //     axisxlabels :  xdata
33336                 //     //yvalues : cols,
33337                    
33338                 // });
33339 //        var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
33340 //        
33341 //        this.load(null,xdata,{
33342 //                axis : "0 0 1 1",
33343 //                axisxlabels :  xdata
33344 //                });
33345
33346     },
33347
33348     load : function(graphtype,xdata,opts)
33349     {
33350         this.raphael.clear();
33351         if(!graphtype) {
33352             graphtype = this.graphtype;
33353         }
33354         if(!opts){
33355             opts = this.opts;
33356         }
33357         var r = this.raphael,
33358             fin = function () {
33359                 this.flag = r.popup(this.bar.x, this.bar.y, this.bar.value || "0").insertBefore(this);
33360             },
33361             fout = function () {
33362                 this.flag.animate({opacity: 0}, 300, function () {this.remove();});
33363             },
33364             pfin = function() {
33365                 this.sector.stop();
33366                 this.sector.scale(1.1, 1.1, this.cx, this.cy);
33367
33368                 if (this.label) {
33369                     this.label[0].stop();
33370                     this.label[0].attr({ r: 7.5 });
33371                     this.label[1].attr({ "font-weight": 800 });
33372                 }
33373             },
33374             pfout = function() {
33375                 this.sector.animate({ transform: 's1 1 ' + this.cx + ' ' + this.cy }, 500, "bounce");
33376
33377                 if (this.label) {
33378                     this.label[0].animate({ r: 5 }, 500, "bounce");
33379                     this.label[1].attr({ "font-weight": 400 });
33380                 }
33381             };
33382
33383         switch(graphtype){
33384             case 'bar':
33385                 this.raphael.barchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
33386                 break;
33387             case 'hbar':
33388                 this.raphael.hbarchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
33389                 break;
33390             case 'pie':
33391 //                opts = { legend: ["%% - Enterprise Users", "% - ddd","Chrome Users"], legendpos: "west", 
33392 //                href: ["http://raphaeljs.com", "http://g.raphaeljs.com"]};
33393 //            
33394                 this.raphael.piechart(this.g_x,this.g_y,this.g_r,xdata,opts).hover(pfin, pfout);
33395                 
33396                 break;
33397
33398         }
33399         
33400         if(this.title){
33401             this.raphael.text(this.title.x, this.title.y, this.title.text).attr(this.title.attr);
33402         }
33403         
33404     },
33405     
33406     setTitle: function(o)
33407     {
33408         this.title = o;
33409     },
33410     
33411     initEvents: function() {
33412         
33413         if(!this.href){
33414             this.el.on('click', this.onClick, this);
33415         }
33416     },
33417     
33418     onClick : function(e)
33419     {
33420         Roo.log('img onclick');
33421         this.fireEvent('click', this, e);
33422     }
33423    
33424 });
33425
33426  
33427 Roo.bootstrap.dash = {};/*
33428  * - LGPL
33429  *
33430  * numberBox
33431  * 
33432  */
33433 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
33434
33435 /**
33436  * @class Roo.bootstrap.dash.NumberBox
33437  * @extends Roo.bootstrap.Component
33438  * Bootstrap NumberBox class
33439  * @cfg {String} headline Box headline
33440  * @cfg {String} content Box content
33441  * @cfg {String} icon Box icon
33442  * @cfg {String} footer Footer text
33443  * @cfg {String} fhref Footer href
33444  * 
33445  * @constructor
33446  * Create a new NumberBox
33447  * @param {Object} config The config object
33448  */
33449
33450
33451 Roo.bootstrap.dash.NumberBox = function(config){
33452     Roo.bootstrap.dash.NumberBox.superclass.constructor.call(this, config);
33453     
33454 };
33455
33456 Roo.extend(Roo.bootstrap.dash.NumberBox, Roo.bootstrap.Component,  {
33457     
33458     headline : '',
33459     content : '',
33460     icon : '',
33461     footer : '',
33462     fhref : '',
33463     ficon : '',
33464     
33465     getAutoCreate : function(){
33466         
33467         var cfg = {
33468             tag : 'div',
33469             cls : 'small-box ',
33470             cn : [
33471                 {
33472                     tag : 'div',
33473                     cls : 'inner',
33474                     cn :[
33475                         {
33476                             tag : 'h3',
33477                             cls : 'roo-headline',
33478                             html : this.headline
33479                         },
33480                         {
33481                             tag : 'p',
33482                             cls : 'roo-content',
33483                             html : this.content
33484                         }
33485                     ]
33486                 }
33487             ]
33488         };
33489         
33490         if(this.icon){
33491             cfg.cn.push({
33492                 tag : 'div',
33493                 cls : 'icon',
33494                 cn :[
33495                     {
33496                         tag : 'i',
33497                         cls : 'ion ' + this.icon
33498                     }
33499                 ]
33500             });
33501         }
33502         
33503         if(this.footer){
33504             var footer = {
33505                 tag : 'a',
33506                 cls : 'small-box-footer',
33507                 href : this.fhref || '#',
33508                 html : this.footer
33509             };
33510             
33511             cfg.cn.push(footer);
33512             
33513         }
33514         
33515         return  cfg;
33516     },
33517
33518     onRender : function(ct,position){
33519         Roo.bootstrap.dash.NumberBox.superclass.onRender.call(this,ct,position);
33520
33521
33522        
33523                 
33524     },
33525
33526     setHeadline: function (value)
33527     {
33528         this.el.select('.roo-headline',true).first().dom.innerHTML = value;
33529     },
33530     
33531     setFooter: function (value, href)
33532     {
33533         this.el.select('a.small-box-footer',true).first().dom.innerHTML = value;
33534         
33535         if(href){
33536             this.el.select('a.small-box-footer',true).first().attr('href', href);
33537         }
33538         
33539     },
33540
33541     setContent: function (value)
33542     {
33543         this.el.select('.roo-content',true).first().dom.innerHTML = value;
33544     },
33545
33546     initEvents: function() 
33547     {   
33548         
33549     }
33550     
33551 });
33552
33553  
33554 /*
33555  * - LGPL
33556  *
33557  * TabBox
33558  * 
33559  */
33560 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
33561
33562 /**
33563  * @class Roo.bootstrap.dash.TabBox
33564  * @extends Roo.bootstrap.Component
33565  * @children Roo.bootstrap.dash.TabPane
33566  * Bootstrap TabBox class
33567  * @cfg {String} title Title of the TabBox
33568  * @cfg {String} icon Icon of the TabBox
33569  * @cfg {Boolean} showtabs (true|false) show the tabs default true
33570  * @cfg {Boolean} tabScrollable (true|false) tab scrollable when mobile view default false
33571  * 
33572  * @constructor
33573  * Create a new TabBox
33574  * @param {Object} config The config object
33575  */
33576
33577
33578 Roo.bootstrap.dash.TabBox = function(config){
33579     Roo.bootstrap.dash.TabBox.superclass.constructor.call(this, config);
33580     this.addEvents({
33581         // raw events
33582         /**
33583          * @event addpane
33584          * When a pane is added
33585          * @param {Roo.bootstrap.dash.TabPane} pane
33586          */
33587         "addpane" : true,
33588         /**
33589          * @event activatepane
33590          * When a pane is activated
33591          * @param {Roo.bootstrap.dash.TabPane} pane
33592          */
33593         "activatepane" : true
33594         
33595          
33596     });
33597     
33598     this.panes = [];
33599 };
33600
33601 Roo.extend(Roo.bootstrap.dash.TabBox, Roo.bootstrap.Component,  {
33602
33603     title : '',
33604     icon : false,
33605     showtabs : true,
33606     tabScrollable : false,
33607     
33608     getChildContainer : function()
33609     {
33610         return this.el.select('.tab-content', true).first();
33611     },
33612     
33613     getAutoCreate : function(){
33614         
33615         var header = {
33616             tag: 'li',
33617             cls: 'pull-left header',
33618             html: this.title,
33619             cn : []
33620         };
33621         
33622         if(this.icon){
33623             header.cn.push({
33624                 tag: 'i',
33625                 cls: 'fa ' + this.icon
33626             });
33627         }
33628         
33629         var h = {
33630             tag: 'ul',
33631             cls: 'nav nav-tabs pull-right',
33632             cn: [
33633                 header
33634             ]
33635         };
33636         
33637         if(this.tabScrollable){
33638             h = {
33639                 tag: 'div',
33640                 cls: 'tab-header',
33641                 cn: [
33642                     {
33643                         tag: 'ul',
33644                         cls: 'nav nav-tabs pull-right',
33645                         cn: [
33646                             header
33647                         ]
33648                     }
33649                 ]
33650             };
33651         }
33652         
33653         var cfg = {
33654             tag: 'div',
33655             cls: 'nav-tabs-custom',
33656             cn: [
33657                 h,
33658                 {
33659                     tag: 'div',
33660                     cls: 'tab-content no-padding',
33661                     cn: []
33662                 }
33663             ]
33664         };
33665
33666         return  cfg;
33667     },
33668     initEvents : function()
33669     {
33670         //Roo.log('add add pane handler');
33671         this.on('addpane', this.onAddPane, this);
33672     },
33673      /**
33674      * Updates the box title
33675      * @param {String} html to set the title to.
33676      */
33677     setTitle : function(value)
33678     {
33679         this.el.select('.nav-tabs .header', true).first().dom.innerHTML = value;
33680     },
33681     onAddPane : function(pane)
33682     {
33683         this.panes.push(pane);
33684         //Roo.log('addpane');
33685         //Roo.log(pane);
33686         // tabs are rendere left to right..
33687         if(!this.showtabs){
33688             return;
33689         }
33690         
33691         var ctr = this.el.select('.nav-tabs', true).first();
33692          
33693          
33694         var existing = ctr.select('.nav-tab',true);
33695         var qty = existing.getCount();;
33696         
33697         
33698         var tab = ctr.createChild({
33699             tag : 'li',
33700             cls : 'nav-tab' + (qty ? '' : ' active'),
33701             cn : [
33702                 {
33703                     tag : 'a',
33704                     href:'#',
33705                     html : pane.title
33706                 }
33707             ]
33708         }, qty ? existing.first().dom : ctr.select('.header', true).first().dom );
33709         pane.tab = tab;
33710         
33711         tab.on('click', this.onTabClick.createDelegate(this, [pane], true));
33712         if (!qty) {
33713             pane.el.addClass('active');
33714         }
33715         
33716                 
33717     },
33718     onTabClick : function(ev,un,ob,pane)
33719     {
33720         //Roo.log('tab - prev default');
33721         ev.preventDefault();
33722         
33723         
33724         this.el.select('.nav-tabs li.nav-tab', true).removeClass('active');
33725         pane.tab.addClass('active');
33726         //Roo.log(pane.title);
33727         this.getChildContainer().select('.tab-pane',true).removeClass('active');
33728         // technically we should have a deactivate event.. but maybe add later.
33729         // and it should not de-activate the selected tab...
33730         this.fireEvent('activatepane', pane);
33731         pane.el.addClass('active');
33732         pane.fireEvent('activate');
33733         
33734         
33735     },
33736     
33737     getActivePane : function()
33738     {
33739         var r = false;
33740         Roo.each(this.panes, function(p) {
33741             if(p.el.hasClass('active')){
33742                 r = p;
33743                 return false;
33744             }
33745             
33746             return;
33747         });
33748         
33749         return r;
33750     }
33751     
33752     
33753 });
33754
33755  
33756 /*
33757  * - LGPL
33758  *
33759  * Tab pane
33760  * 
33761  */
33762 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
33763 /**
33764  * @class Roo.bootstrap.TabPane
33765  * @extends Roo.bootstrap.Component
33766  * @children  Roo.bootstrap.Graph Roo.bootstrap.Column
33767  * Bootstrap TabPane class
33768  * @cfg {Boolean} active (false | true) Default false
33769  * @cfg {String} title title of panel
33770
33771  * 
33772  * @constructor
33773  * Create a new TabPane
33774  * @param {Object} config The config object
33775  */
33776
33777 Roo.bootstrap.dash.TabPane = function(config){
33778     Roo.bootstrap.dash.TabPane.superclass.constructor.call(this, config);
33779     
33780     this.addEvents({
33781         // raw events
33782         /**
33783          * @event activate
33784          * When a pane is activated
33785          * @param {Roo.bootstrap.dash.TabPane} pane
33786          */
33787         "activate" : true
33788          
33789     });
33790 };
33791
33792 Roo.extend(Roo.bootstrap.dash.TabPane, Roo.bootstrap.Component,  {
33793     
33794     active : false,
33795     title : '',
33796     
33797     // the tabBox that this is attached to.
33798     tab : false,
33799      
33800     getAutoCreate : function() 
33801     {
33802         var cfg = {
33803             tag: 'div',
33804             cls: 'tab-pane'
33805         };
33806         
33807         if(this.active){
33808             cfg.cls += ' active';
33809         }
33810         
33811         return cfg;
33812     },
33813     initEvents  : function()
33814     {
33815         //Roo.log('trigger add pane handler');
33816         this.parent().fireEvent('addpane', this)
33817     },
33818     
33819      /**
33820      * Updates the tab title 
33821      * @param {String} html to set the title to.
33822      */
33823     setTitle: function(str)
33824     {
33825         if (!this.tab) {
33826             return;
33827         }
33828         this.title = str;
33829         this.tab.select('a', true).first().dom.innerHTML = str;
33830         
33831     }
33832     
33833     
33834     
33835 });
33836
33837  
33838
33839
33840  /*
33841  * - LGPL
33842  *
33843  * Tooltip
33844  * 
33845  */
33846
33847 /**
33848  * @class Roo.bootstrap.Tooltip
33849  * Bootstrap Tooltip class
33850  * This is basic at present - all componets support it by default, however they should add tooltipEl() method
33851  * to determine which dom element triggers the tooltip.
33852  * 
33853  * It needs to add support for additional attributes like tooltip-position
33854  * 
33855  * @constructor
33856  * Create a new Toolti
33857  * @param {Object} config The config object
33858  */
33859
33860 Roo.bootstrap.Tooltip = function(config){
33861     Roo.bootstrap.Tooltip.superclass.constructor.call(this, config);
33862     
33863     this.alignment = Roo.bootstrap.Tooltip.alignment;
33864     
33865     if(typeof(config) != 'undefined' && typeof(config.alignment) != 'undefined'){
33866         this.alignment = config.alignment;
33867     }
33868     
33869 };
33870
33871 Roo.apply(Roo.bootstrap.Tooltip, {
33872     /**
33873      * @function init initialize tooltip monitoring.
33874      * @static
33875      */
33876     currentEl : false,
33877     currentTip : false,
33878     currentRegion : false,
33879     
33880     //  init : delay?
33881     
33882     init : function()
33883     {
33884         Roo.get(document).on('mouseover', this.enter ,this);
33885         Roo.get(document).on('mouseout', this.leave, this);
33886          
33887         
33888         this.currentTip = new Roo.bootstrap.Tooltip();
33889     },
33890     
33891     enter : function(ev)
33892     {
33893         var dom = ev.getTarget();
33894         
33895         //Roo.log(['enter',dom]);
33896         var el = Roo.fly(dom);
33897         if (this.currentEl) {
33898             //Roo.log(dom);
33899             //Roo.log(this.currentEl);
33900             //Roo.log(this.currentEl.contains(dom));
33901             if (this.currentEl == el) {
33902                 return;
33903             }
33904             if (dom != this.currentEl.dom && this.currentEl.contains(dom)) {
33905                 return;
33906             }
33907
33908         }
33909         
33910         if (this.currentTip.el) {
33911             this.currentTip.el.setVisibilityMode(Roo.Element.DISPLAY).hide(); // force hiding...
33912         }    
33913         //Roo.log(ev);
33914         
33915         if(!el || el.dom == document){
33916             return;
33917         }
33918         
33919         var bindEl = el; 
33920         var pel = false;
33921         if (!el.attr('tooltip')) {
33922             pel = el.findParent("[tooltip]");
33923             if (pel) {
33924                 bindEl = Roo.get(pel);
33925             }
33926         }
33927         
33928        
33929         
33930         // you can not look for children, as if el is the body.. then everythign is the child..
33931         if (!pel && !el.attr('tooltip')) { //
33932             if (!el.select("[tooltip]").elements.length) {
33933                 return;
33934             }
33935             // is the mouse over this child...?
33936             bindEl = el.select("[tooltip]").first();
33937             var xy = ev.getXY();
33938             if (!bindEl.getRegion().contains( { top : xy[1] ,right : xy[0] , bottom : xy[1], left : xy[0]})) {
33939                 //Roo.log("not in region.");
33940                 return;
33941             }
33942             //Roo.log("child element over..");
33943             
33944         }
33945         this.currentEl = el;
33946         this.currentTip.bind(bindEl);
33947         this.currentRegion = Roo.lib.Region.getRegion(dom);
33948         this.currentTip.enter();
33949         
33950     },
33951     leave : function(ev)
33952     {
33953         var dom = ev.getTarget();
33954         //Roo.log(['leave',dom]);
33955         if (!this.currentEl) {
33956             return;
33957         }
33958         
33959         
33960         if (dom != this.currentEl.dom) {
33961             return;
33962         }
33963         var xy = ev.getXY();
33964         if (this.currentRegion.contains( new Roo.lib.Region( xy[1], xy[0] ,xy[1], xy[0]  ))) {
33965             return;
33966         }
33967         // only activate leave if mouse cursor is outside... bounding box..
33968         
33969         
33970         
33971         
33972         if (this.currentTip) {
33973             this.currentTip.leave();
33974         }
33975         //Roo.log('clear currentEl');
33976         this.currentEl = false;
33977         
33978         
33979     },
33980     alignment : {
33981         'left' : ['r-l', [-2,0], 'right'],
33982         'right' : ['l-r', [2,0], 'left'],
33983         'bottom' : ['t-b', [0,2], 'top'],
33984         'top' : [ 'b-t', [0,-2], 'bottom']
33985     }
33986     
33987 });
33988
33989
33990 Roo.extend(Roo.bootstrap.Tooltip, Roo.bootstrap.Component,  {
33991     
33992     
33993     bindEl : false,
33994     
33995     delay : null, // can be { show : 300 , hide: 500}
33996     
33997     timeout : null,
33998     
33999     hoverState : null, //???
34000     
34001     placement : 'bottom', 
34002     
34003     alignment : false,
34004     
34005     getAutoCreate : function(){
34006     
34007         var cfg = {
34008            cls : 'tooltip',   
34009            role : 'tooltip',
34010            cn : [
34011                 {
34012                     cls : 'tooltip-arrow arrow'
34013                 },
34014                 {
34015                     cls : 'tooltip-inner'
34016                 }
34017            ]
34018         };
34019         
34020         return cfg;
34021     },
34022     bind : function(el)
34023     {
34024         this.bindEl = el;
34025     },
34026     
34027     initEvents : function()
34028     {
34029         this.arrowEl = this.el.select('.arrow', true).first();
34030         this.innerEl = this.el.select('.tooltip-inner', true).first();
34031     },
34032     
34033     enter : function () {
34034        
34035         if (this.timeout != null) {
34036             clearTimeout(this.timeout);
34037         }
34038         
34039         this.hoverState = 'in';
34040          //Roo.log("enter - show");
34041         if (!this.delay || !this.delay.show) {
34042             this.show();
34043             return;
34044         }
34045         var _t = this;
34046         this.timeout = setTimeout(function () {
34047             if (_t.hoverState == 'in') {
34048                 _t.show();
34049             }
34050         }, this.delay.show);
34051     },
34052     leave : function()
34053     {
34054         clearTimeout(this.timeout);
34055     
34056         this.hoverState = 'out';
34057          if (!this.delay || !this.delay.hide) {
34058             this.hide();
34059             return;
34060         }
34061        
34062         var _t = this;
34063         this.timeout = setTimeout(function () {
34064             //Roo.log("leave - timeout");
34065             
34066             if (_t.hoverState == 'out') {
34067                 _t.hide();
34068                 Roo.bootstrap.Tooltip.currentEl = false;
34069             }
34070         }, delay);
34071     },
34072     
34073     show : function (msg)
34074     {
34075         if (!this.el) {
34076             this.render(document.body);
34077         }
34078         // set content.
34079         //Roo.log([this.bindEl, this.bindEl.attr('tooltip')]);
34080         
34081         var tip = msg || this.bindEl.attr('tooltip') || this.bindEl.select("[tooltip]").first().attr('tooltip');
34082         
34083         this.el.select('.tooltip-inner',true).first().dom.innerHTML = tip;
34084         
34085         this.el.removeClass(['fade','top','bottom', 'left', 'right','in',
34086                              'bs-tooltip-top','bs-tooltip-bottom', 'bs-tooltip-left', 'bs-tooltip-right']);
34087
34088         if(this.bindEl.attr('tooltip-class')) {
34089             this.el.addClass(this.bindEl.attr('tooltip-class'));
34090         }
34091         
34092         var placement = typeof this.placement == 'function' ?
34093             this.placement.call(this, this.el, on_el) :
34094             this.placement;
34095         
34096         if(this.bindEl.attr('tooltip-placement')) {
34097             placement = this.bindEl.attr('tooltip-placement');
34098         }
34099             
34100         var autoToken = /\s?auto?\s?/i;
34101         var autoPlace = autoToken.test(placement);
34102         if (autoPlace) {
34103             placement = placement.replace(autoToken, '') || 'top';
34104         }
34105         
34106         //this.el.detach()
34107         //this.el.setXY([0,0]);
34108         this.el.show();
34109         //this.el.dom.style.display='block';
34110         
34111         //this.el.appendTo(on_el);
34112         
34113         var p = this.getPosition();
34114         var box = this.el.getBox();
34115         
34116         if (autoPlace) {
34117             // fixme..
34118         }
34119         
34120         var align = this.alignment[placement];
34121         
34122         var xy = this.el.getAlignToXY(this.bindEl, align[0], align[1]);
34123         
34124         if(placement == 'top' || placement == 'bottom'){
34125             if(xy[0] < 0){
34126                 placement = 'right';
34127             }
34128             
34129             if(xy[0] + this.el.getWidth() > Roo.lib.Dom.getViewWidth()){
34130                 placement = 'left';
34131             }
34132             
34133             var scroll = Roo.select('body', true).first().getScroll();
34134             
34135             if(xy[1] > Roo.lib.Dom.getViewHeight() + scroll.top - this.el.getHeight()){
34136                 placement = 'top';
34137             }
34138             
34139             align = this.alignment[placement];
34140             
34141             this.arrowEl.setLeft((this.innerEl.getWidth()/2) - 5);
34142             
34143         }
34144         
34145         var elems = document.getElementsByTagName('div');
34146         var highest = Number.MIN_SAFE_INTEGER || -(Math.pow(2, 53) - 1);
34147         for (var i = 0; i < elems.length; i++) {
34148           var zindex = Number.parseInt(
34149                 document.defaultView.getComputedStyle(elems[i], null).getPropertyValue("z-index"),
34150                 10
34151           );
34152           if (zindex > highest) {
34153             highest = zindex;
34154           }
34155         }
34156         
34157         
34158         
34159         this.el.dom.style.zIndex = highest;
34160         
34161         this.el.alignTo(this.bindEl, align[0],align[1]);
34162         //var arrow = this.el.select('.arrow',true).first();
34163         //arrow.set(align[2], 
34164         
34165         this.el.addClass(placement);
34166         this.el.addClass("bs-tooltip-"+ placement);
34167         
34168         this.el.addClass('in fade show');
34169         
34170         this.hoverState = null;
34171         
34172         if (this.el.hasClass('fade')) {
34173             // fade it?
34174         }
34175         
34176         
34177         
34178         
34179         
34180     },
34181     hide : function()
34182     {
34183          
34184         if (!this.el) {
34185             return;
34186         }
34187         //this.el.setXY([0,0]);
34188         if(this.bindEl.attr('tooltip-class')) {
34189             this.el.removeClass(this.bindEl.attr('tooltip-class'));
34190         }
34191         this.el.removeClass(['show', 'in']);
34192         //this.el.hide();
34193         
34194     }
34195     
34196 });
34197  
34198
34199  /*
34200  * - LGPL
34201  *
34202  * Location Picker
34203  * 
34204  */
34205
34206 /**
34207  * @class Roo.bootstrap.LocationPicker
34208  * @extends Roo.bootstrap.Component
34209  * Bootstrap LocationPicker class
34210  * @cfg {Number} latitude Position when init default 0
34211  * @cfg {Number} longitude Position when init default 0
34212  * @cfg {Number} zoom default 15
34213  * @cfg {String} mapTypeId default google.maps.MapTypeId.ROADMAP
34214  * @cfg {Boolean} mapTypeControl default false
34215  * @cfg {Boolean} disableDoubleClickZoom default false
34216  * @cfg {Boolean} scrollwheel default true
34217  * @cfg {Boolean} streetViewControl default false
34218  * @cfg {Number} radius default 0
34219  * @cfg {String} locationName
34220  * @cfg {Boolean} draggable default true
34221  * @cfg {Boolean} enableAutocomplete default false
34222  * @cfg {Boolean} enableReverseGeocode default true
34223  * @cfg {String} markerTitle
34224  * 
34225  * @constructor
34226  * Create a new LocationPicker
34227  * @param {Object} config The config object
34228  */
34229
34230
34231 Roo.bootstrap.LocationPicker = function(config){
34232     
34233     Roo.bootstrap.LocationPicker.superclass.constructor.call(this, config);
34234     
34235     this.addEvents({
34236         /**
34237          * @event initial
34238          * Fires when the picker initialized.
34239          * @param {Roo.bootstrap.LocationPicker} this
34240          * @param {Google Location} location
34241          */
34242         initial : true,
34243         /**
34244          * @event positionchanged
34245          * Fires when the picker position changed.
34246          * @param {Roo.bootstrap.LocationPicker} this
34247          * @param {Google Location} location
34248          */
34249         positionchanged : true,
34250         /**
34251          * @event resize
34252          * Fires when the map resize.
34253          * @param {Roo.bootstrap.LocationPicker} this
34254          */
34255         resize : true,
34256         /**
34257          * @event show
34258          * Fires when the map show.
34259          * @param {Roo.bootstrap.LocationPicker} this
34260          */
34261         show : true,
34262         /**
34263          * @event hide
34264          * Fires when the map hide.
34265          * @param {Roo.bootstrap.LocationPicker} this
34266          */
34267         hide : true,
34268         /**
34269          * @event mapClick
34270          * Fires when click the map.
34271          * @param {Roo.bootstrap.LocationPicker} this
34272          * @param {Map event} e
34273          */
34274         mapClick : true,
34275         /**
34276          * @event mapRightClick
34277          * Fires when right click the map.
34278          * @param {Roo.bootstrap.LocationPicker} this
34279          * @param {Map event} e
34280          */
34281         mapRightClick : true,
34282         /**
34283          * @event markerClick
34284          * Fires when click the marker.
34285          * @param {Roo.bootstrap.LocationPicker} this
34286          * @param {Map event} e
34287          */
34288         markerClick : true,
34289         /**
34290          * @event markerRightClick
34291          * Fires when right click the marker.
34292          * @param {Roo.bootstrap.LocationPicker} this
34293          * @param {Map event} e
34294          */
34295         markerRightClick : true,
34296         /**
34297          * @event OverlayViewDraw
34298          * Fires when OverlayView Draw
34299          * @param {Roo.bootstrap.LocationPicker} this
34300          */
34301         OverlayViewDraw : true,
34302         /**
34303          * @event OverlayViewOnAdd
34304          * Fires when OverlayView Draw
34305          * @param {Roo.bootstrap.LocationPicker} this
34306          */
34307         OverlayViewOnAdd : true,
34308         /**
34309          * @event OverlayViewOnRemove
34310          * Fires when OverlayView Draw
34311          * @param {Roo.bootstrap.LocationPicker} this
34312          */
34313         OverlayViewOnRemove : true,
34314         /**
34315          * @event OverlayViewShow
34316          * Fires when OverlayView Draw
34317          * @param {Roo.bootstrap.LocationPicker} this
34318          * @param {Pixel} cpx
34319          */
34320         OverlayViewShow : true,
34321         /**
34322          * @event OverlayViewHide
34323          * Fires when OverlayView Draw
34324          * @param {Roo.bootstrap.LocationPicker} this
34325          */
34326         OverlayViewHide : true,
34327         /**
34328          * @event loadexception
34329          * Fires when load google lib failed.
34330          * @param {Roo.bootstrap.LocationPicker} this
34331          */
34332         loadexception : true
34333     });
34334         
34335 };
34336
34337 Roo.extend(Roo.bootstrap.LocationPicker, Roo.bootstrap.Component,  {
34338     
34339     gMapContext: false,
34340     
34341     latitude: 0,
34342     longitude: 0,
34343     zoom: 15,
34344     mapTypeId: false,
34345     mapTypeControl: false,
34346     disableDoubleClickZoom: false,
34347     scrollwheel: true,
34348     streetViewControl: false,
34349     radius: 0,
34350     locationName: '',
34351     draggable: true,
34352     enableAutocomplete: false,
34353     enableReverseGeocode: true,
34354     markerTitle: '',
34355     
34356     getAutoCreate: function()
34357     {
34358
34359         var cfg = {
34360             tag: 'div',
34361             cls: 'roo-location-picker'
34362         };
34363         
34364         return cfg
34365     },
34366     
34367     initEvents: function(ct, position)
34368     {       
34369         if(!this.el.getWidth() || this.isApplied()){
34370             return;
34371         }
34372         
34373         this.el.setVisibilityMode(Roo.Element.DISPLAY);
34374         
34375         this.initial();
34376     },
34377     
34378     initial: function()
34379     {
34380         if(typeof(google) == 'undefined' || typeof(google.maps) == 'undefined'){
34381             this.fireEvent('loadexception', this);
34382             return;
34383         }
34384         
34385         if(!this.mapTypeId){
34386             this.mapTypeId = google.maps.MapTypeId.ROADMAP;
34387         }
34388         
34389         this.gMapContext = this.GMapContext();
34390         
34391         this.initOverlayView();
34392         
34393         this.OverlayView = new Roo.bootstrap.LocationPicker.OverlayView(this.gMapContext.map);
34394         
34395         var _this = this;
34396                 
34397         google.maps.event.addListener(this.gMapContext.marker, "dragend", function(event) {
34398             _this.setPosition(_this.gMapContext.marker.position);
34399         });
34400         
34401         google.maps.event.addListener(this.gMapContext.map, 'click', function(event){
34402             _this.fireEvent('mapClick', this, event);
34403             
34404         });
34405
34406         google.maps.event.addListener(this.gMapContext.map, 'rightclick', function(event){
34407             _this.fireEvent('mapRightClick', this, event);
34408             
34409         });
34410         
34411         google.maps.event.addListener(this.gMapContext.marker, 'click', function(event){
34412             _this.fireEvent('markerClick', this, event);
34413             
34414         });
34415
34416         google.maps.event.addListener(this.gMapContext.marker, 'rightclick', function(event){
34417             _this.fireEvent('markerRightClick', this, event);
34418             
34419         });
34420         
34421         this.setPosition(this.gMapContext.location);
34422         
34423         this.fireEvent('initial', this, this.gMapContext.location);
34424     },
34425     
34426     initOverlayView: function()
34427     {
34428         var _this = this;
34429         
34430         Roo.bootstrap.LocationPicker.OverlayView.prototype = Roo.apply(new google.maps.OverlayView(), {
34431             
34432             draw: function()
34433             {
34434                 _this.fireEvent('OverlayViewDraw', _this);
34435             },
34436             
34437             onAdd: function()
34438             {
34439                 _this.fireEvent('OverlayViewOnAdd', _this);
34440             },
34441             
34442             onRemove: function()
34443             {
34444                 _this.fireEvent('OverlayViewOnRemove', _this);
34445             },
34446             
34447             show: function(cpx)
34448             {
34449                 _this.fireEvent('OverlayViewShow', _this, cpx);
34450             },
34451             
34452             hide: function()
34453             {
34454                 _this.fireEvent('OverlayViewHide', _this);
34455             }
34456             
34457         });
34458     },
34459     
34460     fromLatLngToContainerPixel: function(event)
34461     {
34462         return this.OverlayView.getProjection().fromLatLngToContainerPixel(event.latLng);
34463     },
34464     
34465     isApplied: function() 
34466     {
34467         return this.getGmapContext() == false ? false : true;
34468     },
34469     
34470     getGmapContext: function() 
34471     {
34472         return (typeof(this.gMapContext) == 'undefined') ? false : this.gMapContext;
34473     },
34474     
34475     GMapContext: function() 
34476     {
34477         var position = new google.maps.LatLng(this.latitude, this.longitude);
34478         
34479         var _map = new google.maps.Map(this.el.dom, {
34480             center: position,
34481             zoom: this.zoom,
34482             mapTypeId: this.mapTypeId,
34483             mapTypeControl: this.mapTypeControl,
34484             disableDoubleClickZoom: this.disableDoubleClickZoom,
34485             scrollwheel: this.scrollwheel,
34486             streetViewControl: this.streetViewControl,
34487             locationName: this.locationName,
34488             draggable: this.draggable,
34489             enableAutocomplete: this.enableAutocomplete,
34490             enableReverseGeocode: this.enableReverseGeocode
34491         });
34492         
34493         var _marker = new google.maps.Marker({
34494             position: position,
34495             map: _map,
34496             title: this.markerTitle,
34497             draggable: this.draggable
34498         });
34499         
34500         return {
34501             map: _map,
34502             marker: _marker,
34503             circle: null,
34504             location: position,
34505             radius: this.radius,
34506             locationName: this.locationName,
34507             addressComponents: {
34508                 formatted_address: null,
34509                 addressLine1: null,
34510                 addressLine2: null,
34511                 streetName: null,
34512                 streetNumber: null,
34513                 city: null,
34514                 district: null,
34515                 state: null,
34516                 stateOrProvince: null
34517             },
34518             settings: this,
34519             domContainer: this.el.dom,
34520             geodecoder: new google.maps.Geocoder()
34521         };
34522     },
34523     
34524     drawCircle: function(center, radius, options) 
34525     {
34526         if (this.gMapContext.circle != null) {
34527             this.gMapContext.circle.setMap(null);
34528         }
34529         if (radius > 0) {
34530             radius *= 1;
34531             options = Roo.apply({}, options, {
34532                 strokeColor: "#0000FF",
34533                 strokeOpacity: .35,
34534                 strokeWeight: 2,
34535                 fillColor: "#0000FF",
34536                 fillOpacity: .2
34537             });
34538             
34539             options.map = this.gMapContext.map;
34540             options.radius = radius;
34541             options.center = center;
34542             this.gMapContext.circle = new google.maps.Circle(options);
34543             return this.gMapContext.circle;
34544         }
34545         
34546         return null;
34547     },
34548     
34549     setPosition: function(location) 
34550     {
34551         this.gMapContext.location = location;
34552         this.gMapContext.marker.setPosition(location);
34553         this.gMapContext.map.panTo(location);
34554         this.drawCircle(location, this.gMapContext.radius, {});
34555         
34556         var _this = this;
34557         
34558         if (this.gMapContext.settings.enableReverseGeocode) {
34559             this.gMapContext.geodecoder.geocode({
34560                 latLng: this.gMapContext.location
34561             }, function(results, status) {
34562                 
34563                 if (status == google.maps.GeocoderStatus.OK && results.length > 0) {
34564                     _this.gMapContext.locationName = results[0].formatted_address;
34565                     _this.gMapContext.addressComponents = _this.address_component_from_google_geocode(results[0].address_components);
34566                     
34567                     _this.fireEvent('positionchanged', this, location);
34568                 }
34569             });
34570             
34571             return;
34572         }
34573         
34574         this.fireEvent('positionchanged', this, location);
34575     },
34576     
34577     resize: function()
34578     {
34579         google.maps.event.trigger(this.gMapContext.map, "resize");
34580         
34581         this.gMapContext.map.setCenter(this.gMapContext.marker.position);
34582         
34583         this.fireEvent('resize', this);
34584     },
34585     
34586     setPositionByLatLng: function(latitude, longitude)
34587     {
34588         this.setPosition(new google.maps.LatLng(latitude, longitude));
34589     },
34590     
34591     getCurrentPosition: function() 
34592     {
34593         return {
34594             latitude: this.gMapContext.location.lat(),
34595             longitude: this.gMapContext.location.lng()
34596         };
34597     },
34598     
34599     getAddressName: function() 
34600     {
34601         return this.gMapContext.locationName;
34602     },
34603     
34604     getAddressComponents: function() 
34605     {
34606         return this.gMapContext.addressComponents;
34607     },
34608     
34609     address_component_from_google_geocode: function(address_components) 
34610     {
34611         var result = {};
34612         
34613         for (var i = 0; i < address_components.length; i++) {
34614             var component = address_components[i];
34615             if (component.types.indexOf("postal_code") >= 0) {
34616                 result.postalCode = component.short_name;
34617             } else if (component.types.indexOf("street_number") >= 0) {
34618                 result.streetNumber = component.short_name;
34619             } else if (component.types.indexOf("route") >= 0) {
34620                 result.streetName = component.short_name;
34621             } else if (component.types.indexOf("neighborhood") >= 0) {
34622                 result.city = component.short_name;
34623             } else if (component.types.indexOf("locality") >= 0) {
34624                 result.city = component.short_name;
34625             } else if (component.types.indexOf("sublocality") >= 0) {
34626                 result.district = component.short_name;
34627             } else if (component.types.indexOf("administrative_area_level_1") >= 0) {
34628                 result.stateOrProvince = component.short_name;
34629             } else if (component.types.indexOf("country") >= 0) {
34630                 result.country = component.short_name;
34631             }
34632         }
34633         
34634         result.addressLine1 = [ result.streetNumber, result.streetName ].join(" ").trim();
34635         result.addressLine2 = "";
34636         return result;
34637     },
34638     
34639     setZoomLevel: function(zoom)
34640     {
34641         this.gMapContext.map.setZoom(zoom);
34642     },
34643     
34644     show: function()
34645     {
34646         if(!this.el){
34647             return;
34648         }
34649         
34650         this.el.show();
34651         
34652         this.resize();
34653         
34654         this.fireEvent('show', this);
34655     },
34656     
34657     hide: function()
34658     {
34659         if(!this.el){
34660             return;
34661         }
34662         
34663         this.el.hide();
34664         
34665         this.fireEvent('hide', this);
34666     }
34667     
34668 });
34669
34670 Roo.apply(Roo.bootstrap.LocationPicker, {
34671     
34672     OverlayView : function(map, options)
34673     {
34674         options = options || {};
34675         
34676         this.setMap(map);
34677     }
34678     
34679     
34680 });/**
34681  * @class Roo.bootstrap.Alert
34682  * @extends Roo.bootstrap.Component
34683  * Bootstrap Alert class - shows an alert area box
34684  * eg
34685  * <div class="alert alert-danger" role="alert"><span class="fa fa-exclamation-triangle"></span><span class="sr-only">Error:</span>
34686   Enter a valid email address
34687 </div>
34688  * @licence LGPL
34689  * @cfg {String} title The title of alert
34690  * @cfg {String} html The content of alert
34691  * @cfg {String} weight (success|info|warning|danger) Weight of the message
34692  * @cfg {String} fa font-awesomeicon
34693  * @cfg {Number} seconds default:-1 Number of seconds until it disapears (-1 means never.)
34694  * @cfg {Boolean} close true to show a x closer
34695  * 
34696  * 
34697  * @constructor
34698  * Create a new alert
34699  * @param {Object} config The config object
34700  */
34701
34702
34703 Roo.bootstrap.Alert = function(config){
34704     Roo.bootstrap.Alert.superclass.constructor.call(this, config);
34705     
34706 };
34707
34708 Roo.extend(Roo.bootstrap.Alert, Roo.bootstrap.Component,  {
34709     
34710     title: '',
34711     html: '',
34712     weight: false,
34713     fa: false,
34714     faicon: false, // BC
34715     close : false,
34716     
34717     
34718     getAutoCreate : function()
34719     {
34720         
34721         var cfg = {
34722             tag : 'div',
34723             cls : 'alert',
34724             cn : [
34725                 {
34726                     tag: 'button',
34727                     type :  "button",
34728                     cls: "close",
34729                     html : '×',
34730                     style : this.close ? '' : 'display:none'
34731                 },
34732                 {
34733                     tag : 'i',
34734                     cls : 'roo-alert-icon'
34735                     
34736                 },
34737                 {
34738                     tag : 'b',
34739                     cls : 'roo-alert-title',
34740                     html : this.title
34741                 },
34742                 {
34743                     tag : 'span',
34744                     cls : 'roo-alert-text',
34745                     html : this.html
34746                 }
34747             ]
34748         };
34749         
34750         if(this.faicon){
34751             cfg.cn[0].cls += ' fa ' + this.faicon;
34752         }
34753         if(this.fa){
34754             cfg.cn[0].cls += ' fa ' + this.fa;
34755         }
34756         
34757         if(this.weight){
34758             cfg.cls += ' alert-' + this.weight;
34759         }
34760         
34761         return cfg;
34762     },
34763     
34764     initEvents: function() 
34765     {
34766         this.el.setVisibilityMode(Roo.Element.DISPLAY);
34767         this.titleEl =  this.el.select('.roo-alert-title',true).first();
34768         this.iconEl = this.el.select('.roo-alert-icon',true).first();
34769         this.htmlEl = this.el.select('.roo-alert-text',true).first();
34770         if (this.seconds > 0) {
34771             this.hide.defer(this.seconds, this);
34772         }
34773     },
34774     /**
34775      * Set the Title Message HTML
34776      * @param {String} html
34777      */
34778     setTitle : function(str)
34779     {
34780         this.titleEl.dom.innerHTML = str;
34781     },
34782      
34783      /**
34784      * Set the Body Message HTML
34785      * @param {String} html
34786      */
34787     setHtml : function(str)
34788     {
34789         this.htmlEl.dom.innerHTML = str;
34790     },
34791     /**
34792      * Set the Weight of the alert
34793      * @param {String} (success|info|warning|danger) weight
34794      */
34795     
34796     setWeight : function(weight)
34797     {
34798         if(this.weight){
34799             this.el.removeClass('alert-' + this.weight);
34800         }
34801         
34802         this.weight = weight;
34803         
34804         this.el.addClass('alert-' + this.weight);
34805     },
34806       /**
34807      * Set the Icon of the alert
34808      * @param {String} see fontawsome names (name without the 'fa-' bit)
34809      */
34810     setIcon : function(icon)
34811     {
34812         if(this.faicon){
34813             this.alertEl.removeClass(['fa', 'fa-' + this.faicon]);
34814         }
34815         
34816         this.faicon = icon;
34817         
34818         this.alertEl.addClass(['fa', 'fa-' + this.faicon]);
34819     },
34820     /**
34821      * Hide the Alert
34822      */
34823     hide: function() 
34824     {
34825         this.el.hide();   
34826     },
34827     /**
34828      * Show the Alert
34829      */
34830     show: function() 
34831     {  
34832         this.el.show();   
34833     }
34834     
34835 });
34836
34837  
34838 /*
34839 * Licence: LGPL
34840 */
34841
34842 /**
34843  * @class Roo.bootstrap.UploadCropbox
34844  * @extends Roo.bootstrap.Component
34845  * Bootstrap UploadCropbox class
34846  * @cfg {String} emptyText show when image has been loaded
34847  * @cfg {String} rotateNotify show when image too small to rotate
34848  * @cfg {Number} errorTimeout default 3000
34849  * @cfg {Number} minWidth default 300
34850  * @cfg {Number} minHeight default 300
34851  * @cfg {Array} buttons default ['rotateLeft', 'pictureBtn', 'rotateRight']
34852  * @cfg {Boolean} isDocument (true|false) default false
34853  * @cfg {String} url action url
34854  * @cfg {String} paramName default 'imageUpload'
34855  * @cfg {String} method default POST
34856  * @cfg {Boolean} loadMask (true|false) default true
34857  * @cfg {Boolean} loadingText default 'Loading...'
34858  * 
34859  * @constructor
34860  * Create a new UploadCropbox
34861  * @param {Object} config The config object
34862  */
34863
34864 Roo.bootstrap.UploadCropbox = function(config){
34865     Roo.bootstrap.UploadCropbox.superclass.constructor.call(this, config);
34866     
34867     this.addEvents({
34868         /**
34869          * @event beforeselectfile
34870          * Fire before select file
34871          * @param {Roo.bootstrap.UploadCropbox} this
34872          */
34873         "beforeselectfile" : true,
34874         /**
34875          * @event initial
34876          * Fire after initEvent
34877          * @param {Roo.bootstrap.UploadCropbox} this
34878          */
34879         "initial" : true,
34880         /**
34881          * @event crop
34882          * Fire after initEvent
34883          * @param {Roo.bootstrap.UploadCropbox} this
34884          * @param {String} data
34885          */
34886         "crop" : true,
34887         /**
34888          * @event prepare
34889          * Fire when preparing the file data
34890          * @param {Roo.bootstrap.UploadCropbox} this
34891          * @param {Object} file
34892          */
34893         "prepare" : true,
34894         /**
34895          * @event exception
34896          * Fire when get exception
34897          * @param {Roo.bootstrap.UploadCropbox} this
34898          * @param {XMLHttpRequest} xhr
34899          */
34900         "exception" : true,
34901         /**
34902          * @event beforeloadcanvas
34903          * Fire before load the canvas
34904          * @param {Roo.bootstrap.UploadCropbox} this
34905          * @param {String} src
34906          */
34907         "beforeloadcanvas" : true,
34908         /**
34909          * @event trash
34910          * Fire when trash image
34911          * @param {Roo.bootstrap.UploadCropbox} this
34912          */
34913         "trash" : true,
34914         /**
34915          * @event download
34916          * Fire when download the image
34917          * @param {Roo.bootstrap.UploadCropbox} this
34918          */
34919         "download" : true,
34920         /**
34921          * @event footerbuttonclick
34922          * Fire when footerbuttonclick
34923          * @param {Roo.bootstrap.UploadCropbox} this
34924          * @param {String} type
34925          */
34926         "footerbuttonclick" : true,
34927         /**
34928          * @event resize
34929          * Fire when resize
34930          * @param {Roo.bootstrap.UploadCropbox} this
34931          */
34932         "resize" : true,
34933         /**
34934          * @event rotate
34935          * Fire when rotate the image
34936          * @param {Roo.bootstrap.UploadCropbox} this
34937          * @param {String} pos
34938          */
34939         "rotate" : true,
34940         /**
34941          * @event inspect
34942          * Fire when inspect the file
34943          * @param {Roo.bootstrap.UploadCropbox} this
34944          * @param {Object} file
34945          */
34946         "inspect" : true,
34947         /**
34948          * @event upload
34949          * Fire when xhr upload the file
34950          * @param {Roo.bootstrap.UploadCropbox} this
34951          * @param {Object} data
34952          */
34953         "upload" : true,
34954         /**
34955          * @event arrange
34956          * Fire when arrange the file data
34957          * @param {Roo.bootstrap.UploadCropbox} this
34958          * @param {Object} formData
34959          */
34960         "arrange" : true
34961     });
34962     
34963     this.buttons = this.buttons || Roo.bootstrap.UploadCropbox.footer.STANDARD;
34964 };
34965
34966 Roo.extend(Roo.bootstrap.UploadCropbox, Roo.bootstrap.Component,  {
34967     
34968     emptyText : 'Click to upload image',
34969     rotateNotify : 'Image is too small to rotate',
34970     errorTimeout : 3000,
34971     scale : 0,
34972     baseScale : 1,
34973     rotate : 0,
34974     dragable : false,
34975     pinching : false,
34976     mouseX : 0,
34977     mouseY : 0,
34978     cropData : false,
34979     minWidth : 300,
34980     minHeight : 300,
34981     file : false,
34982     exif : {},
34983     baseRotate : 1,
34984     cropType : 'image/jpeg',
34985     buttons : false,
34986     canvasLoaded : false,
34987     isDocument : false,
34988     method : 'POST',
34989     paramName : 'imageUpload',
34990     loadMask : true,
34991     loadingText : 'Loading...',
34992     maskEl : false,
34993     
34994     getAutoCreate : function()
34995     {
34996         var cfg = {
34997             tag : 'div',
34998             cls : 'roo-upload-cropbox',
34999             cn : [
35000                 {
35001                     tag : 'input',
35002                     cls : 'roo-upload-cropbox-selector',
35003                     type : 'file'
35004                 },
35005                 {
35006                     tag : 'div',
35007                     cls : 'roo-upload-cropbox-body',
35008                     style : 'cursor:pointer',
35009                     cn : [
35010                         {
35011                             tag : 'div',
35012                             cls : 'roo-upload-cropbox-preview'
35013                         },
35014                         {
35015                             tag : 'div',
35016                             cls : 'roo-upload-cropbox-thumb'
35017                         },
35018                         {
35019                             tag : 'div',
35020                             cls : 'roo-upload-cropbox-empty-notify',
35021                             html : this.emptyText
35022                         },
35023                         {
35024                             tag : 'div',
35025                             cls : 'roo-upload-cropbox-error-notify alert alert-danger',
35026                             html : this.rotateNotify
35027                         }
35028                     ]
35029                 },
35030                 {
35031                     tag : 'div',
35032                     cls : 'roo-upload-cropbox-footer',
35033                     cn : {
35034                         tag : 'div',
35035                         cls : 'btn-group btn-group-justified roo-upload-cropbox-btn-group',
35036                         cn : []
35037                     }
35038                 }
35039             ]
35040         };
35041         
35042         return cfg;
35043     },
35044     
35045     onRender : function(ct, position)
35046     {
35047         Roo.bootstrap.UploadCropbox.superclass.onRender.call(this, ct, position);
35048         
35049         if (this.buttons.length) {
35050             
35051             Roo.each(this.buttons, function(bb) {
35052                 
35053                 var btn = this.el.select('.roo-upload-cropbox-footer div.roo-upload-cropbox-btn-group').first().createChild(bb);
35054                 
35055                 btn.on('click', this.onFooterButtonClick.createDelegate(this, [bb.action], true));
35056                 
35057             }, this);
35058         }
35059         
35060         if(this.loadMask){
35061             this.maskEl = this.el;
35062         }
35063     },
35064     
35065     initEvents : function()
35066     {
35067         this.urlAPI = (window.createObjectURL && window) || 
35068                                 (window.URL && URL.revokeObjectURL && URL) || 
35069                                 (window.webkitURL && webkitURL);
35070                         
35071         this.bodyEl = this.el.select('.roo-upload-cropbox-body', true).first();
35072         this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
35073         
35074         this.selectorEl = this.el.select('.roo-upload-cropbox-selector', true).first();
35075         this.selectorEl.hide();
35076         
35077         this.previewEl = this.el.select('.roo-upload-cropbox-preview', true).first();
35078         this.previewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
35079         
35080         this.thumbEl = this.el.select('.roo-upload-cropbox-thumb', true).first();
35081         this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
35082         this.thumbEl.hide();
35083         
35084         this.notifyEl = this.el.select('.roo-upload-cropbox-empty-notify', true).first();
35085         this.notifyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
35086         
35087         this.errorEl = this.el.select('.roo-upload-cropbox-error-notify', true).first();
35088         this.errorEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
35089         this.errorEl.hide();
35090         
35091         this.footerEl = this.el.select('.roo-upload-cropbox-footer', true).first();
35092         this.footerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
35093         this.footerEl.hide();
35094         
35095         this.setThumbBoxSize();
35096         
35097         this.bind();
35098         
35099         this.resize();
35100         
35101         this.fireEvent('initial', this);
35102     },
35103
35104     bind : function()
35105     {
35106         var _this = this;
35107         
35108         window.addEventListener("resize", function() { _this.resize(); } );
35109         
35110         this.bodyEl.on('click', this.beforeSelectFile, this);
35111         
35112         if(Roo.isTouch){
35113             this.bodyEl.on('touchstart', this.onTouchStart, this);
35114             this.bodyEl.on('touchmove', this.onTouchMove, this);
35115             this.bodyEl.on('touchend', this.onTouchEnd, this);
35116         }
35117         
35118         if(!Roo.isTouch){
35119             this.bodyEl.on('mousedown', this.onMouseDown, this);
35120             this.bodyEl.on('mousemove', this.onMouseMove, this);
35121             var mousewheel = (/Firefox/i.test(navigator.userAgent))? 'DOMMouseScroll' : 'mousewheel';
35122             this.bodyEl.on(mousewheel, this.onMouseWheel, this);
35123             Roo.get(document).on('mouseup', this.onMouseUp, this);
35124         }
35125         
35126         this.selectorEl.on('change', this.onFileSelected, this);
35127     },
35128     
35129     reset : function()
35130     {    
35131         this.scale = 0;
35132         this.baseScale = 1;
35133         this.rotate = 0;
35134         this.baseRotate = 1;
35135         this.dragable = false;
35136         this.pinching = false;
35137         this.mouseX = 0;
35138         this.mouseY = 0;
35139         this.cropData = false;
35140         this.notifyEl.dom.innerHTML = this.emptyText;
35141         
35142         this.selectorEl.dom.value = '';
35143         
35144     },
35145     
35146     resize : function()
35147     {
35148         if(this.fireEvent('resize', this) != false){
35149             this.setThumbBoxPosition();
35150             this.setCanvasPosition();
35151         }
35152     },
35153     
35154     onFooterButtonClick : function(e, el, o, type)
35155     {
35156         switch (type) {
35157             case 'rotate-left' :
35158                 this.onRotateLeft(e);
35159                 break;
35160             case 'rotate-right' :
35161                 this.onRotateRight(e);
35162                 break;
35163             case 'picture' :
35164                 this.beforeSelectFile(e);
35165                 break;
35166             case 'trash' :
35167                 this.trash(e);
35168                 break;
35169             case 'crop' :
35170                 this.crop(e);
35171                 break;
35172             case 'download' :
35173                 this.download(e);
35174                 break;
35175             default :
35176                 break;
35177         }
35178         
35179         this.fireEvent('footerbuttonclick', this, type);
35180     },
35181     
35182     beforeSelectFile : function(e)
35183     {
35184         e.preventDefault();
35185         
35186         if(this.fireEvent('beforeselectfile', this) != false){
35187             this.selectorEl.dom.click();
35188         }
35189     },
35190     
35191     onFileSelected : function(e)
35192     {
35193         e.preventDefault();
35194         
35195         if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
35196             return;
35197         }
35198         
35199         var file = this.selectorEl.dom.files[0];
35200         
35201         if(this.fireEvent('inspect', this, file) != false){
35202             this.prepare(file);
35203         }
35204         
35205     },
35206     
35207     trash : function(e)
35208     {
35209         this.fireEvent('trash', this);
35210     },
35211     
35212     download : function(e)
35213     {
35214         this.fireEvent('download', this);
35215     },
35216     
35217     loadCanvas : function(src)
35218     {   
35219         if(this.fireEvent('beforeloadcanvas', this, src) != false){
35220             
35221             this.reset();
35222             
35223             this.imageEl = document.createElement('img');
35224             
35225             var _this = this;
35226             
35227             this.imageEl.addEventListener("load", function(){ _this.onLoadCanvas(); });
35228             
35229             this.imageEl.src = src;
35230         }
35231     },
35232     
35233     onLoadCanvas : function()
35234     {   
35235         this.imageEl.OriginWidth = this.imageEl.naturalWidth || this.imageEl.width;
35236         this.imageEl.OriginHeight = this.imageEl.naturalHeight || this.imageEl.height;
35237         
35238         this.bodyEl.un('click', this.beforeSelectFile, this);
35239         
35240         this.notifyEl.hide();
35241         this.thumbEl.show();
35242         this.footerEl.show();
35243         
35244         this.baseRotateLevel();
35245         
35246         if(this.isDocument){
35247             this.setThumbBoxSize();
35248         }
35249         
35250         this.setThumbBoxPosition();
35251         
35252         this.baseScaleLevel();
35253         
35254         this.draw();
35255         
35256         this.resize();
35257         
35258         this.canvasLoaded = true;
35259         
35260         if(this.loadMask){
35261             this.maskEl.unmask();
35262         }
35263         
35264     },
35265     
35266     setCanvasPosition : function()
35267     {   
35268         if(!this.canvasEl){
35269             return;
35270         }
35271         
35272         var pw = Math.ceil((this.bodyEl.getWidth() - this.canvasEl.width) / 2);
35273         var ph = Math.ceil((this.bodyEl.getHeight() - this.canvasEl.height) / 2);
35274         
35275         this.previewEl.setLeft(pw);
35276         this.previewEl.setTop(ph);
35277         
35278     },
35279     
35280     onMouseDown : function(e)
35281     {   
35282         e.stopEvent();
35283         
35284         this.dragable = true;
35285         this.pinching = false;
35286         
35287         if(this.isDocument && (this.canvasEl.width < this.thumbEl.getWidth() || this.canvasEl.height < this.thumbEl.getHeight())){
35288             this.dragable = false;
35289             return;
35290         }
35291         
35292         this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
35293         this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
35294         
35295     },
35296     
35297     onMouseMove : function(e)
35298     {   
35299         e.stopEvent();
35300         
35301         if(!this.canvasLoaded){
35302             return;
35303         }
35304         
35305         if (!this.dragable){
35306             return;
35307         }
35308         
35309         var minX = Math.ceil(this.thumbEl.getLeft(true));
35310         var minY = Math.ceil(this.thumbEl.getTop(true));
35311         
35312         var maxX = Math.ceil(minX + this.thumbEl.getWidth() - this.canvasEl.width);
35313         var maxY = Math.ceil(minY + this.thumbEl.getHeight() - this.canvasEl.height);
35314         
35315         var x = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
35316         var y = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
35317         
35318         x = x - this.mouseX;
35319         y = y - this.mouseY;
35320         
35321         var bgX = Math.ceil(x + this.previewEl.getLeft(true));
35322         var bgY = Math.ceil(y + this.previewEl.getTop(true));
35323         
35324         bgX = (minX < bgX) ? minX : ((maxX > bgX) ? maxX : bgX);
35325         bgY = (minY < bgY) ? minY : ((maxY > bgY) ? maxY : bgY);
35326         
35327         this.previewEl.setLeft(bgX);
35328         this.previewEl.setTop(bgY);
35329         
35330         this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
35331         this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
35332     },
35333     
35334     onMouseUp : function(e)
35335     {   
35336         e.stopEvent();
35337         
35338         this.dragable = false;
35339     },
35340     
35341     onMouseWheel : function(e)
35342     {   
35343         e.stopEvent();
35344         
35345         this.startScale = this.scale;
35346         
35347         this.scale = (e.getWheelDelta() == 1) ? (this.scale + 1) : (this.scale - 1);
35348         
35349         if(!this.zoomable()){
35350             this.scale = this.startScale;
35351             return;
35352         }
35353         
35354         this.draw();
35355         
35356         return;
35357     },
35358     
35359     zoomable : function()
35360     {
35361         var minScale = this.thumbEl.getWidth() / this.minWidth;
35362         
35363         if(this.minWidth < this.minHeight){
35364             minScale = this.thumbEl.getHeight() / this.minHeight;
35365         }
35366         
35367         var width = Math.ceil(this.imageEl.OriginWidth * this.getScaleLevel() / minScale);
35368         var height = Math.ceil(this.imageEl.OriginHeight * this.getScaleLevel() / minScale);
35369         
35370         if(
35371                 this.isDocument &&
35372                 (this.rotate == 0 || this.rotate == 180) && 
35373                 (
35374                     width > this.imageEl.OriginWidth || 
35375                     height > this.imageEl.OriginHeight ||
35376                     (width < this.minWidth && height < this.minHeight)
35377                 )
35378         ){
35379             return false;
35380         }
35381         
35382         if(
35383                 this.isDocument &&
35384                 (this.rotate == 90 || this.rotate == 270) && 
35385                 (
35386                     width > this.imageEl.OriginWidth || 
35387                     height > this.imageEl.OriginHeight ||
35388                     (width < this.minHeight && height < this.minWidth)
35389                 )
35390         ){
35391             return false;
35392         }
35393         
35394         if(
35395                 !this.isDocument &&
35396                 (this.rotate == 0 || this.rotate == 180) && 
35397                 (
35398                     width < this.minWidth || 
35399                     width > this.imageEl.OriginWidth || 
35400                     height < this.minHeight || 
35401                     height > this.imageEl.OriginHeight
35402                 )
35403         ){
35404             return false;
35405         }
35406         
35407         if(
35408                 !this.isDocument &&
35409                 (this.rotate == 90 || this.rotate == 270) && 
35410                 (
35411                     width < this.minHeight || 
35412                     width > this.imageEl.OriginWidth || 
35413                     height < this.minWidth || 
35414                     height > this.imageEl.OriginHeight
35415                 )
35416         ){
35417             return false;
35418         }
35419         
35420         return true;
35421         
35422     },
35423     
35424     onRotateLeft : function(e)
35425     {   
35426         if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
35427             
35428             var minScale = this.thumbEl.getWidth() / this.minWidth;
35429             
35430             var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
35431             var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
35432             
35433             this.startScale = this.scale;
35434             
35435             while (this.getScaleLevel() < minScale){
35436             
35437                 this.scale = this.scale + 1;
35438                 
35439                 if(!this.zoomable()){
35440                     break;
35441                 }
35442                 
35443                 if(
35444                         Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
35445                         Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
35446                 ){
35447                     continue;
35448                 }
35449                 
35450                 this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
35451
35452                 this.draw();
35453                 
35454                 return;
35455             }
35456             
35457             this.scale = this.startScale;
35458             
35459             this.onRotateFail();
35460             
35461             return false;
35462         }
35463         
35464         this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
35465
35466         if(this.isDocument){
35467             this.setThumbBoxSize();
35468             this.setThumbBoxPosition();
35469             this.setCanvasPosition();
35470         }
35471         
35472         this.draw();
35473         
35474         this.fireEvent('rotate', this, 'left');
35475         
35476     },
35477     
35478     onRotateRight : function(e)
35479     {
35480         if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
35481             
35482             var minScale = this.thumbEl.getWidth() / this.minWidth;
35483         
35484             var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
35485             var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
35486             
35487             this.startScale = this.scale;
35488             
35489             while (this.getScaleLevel() < minScale){
35490             
35491                 this.scale = this.scale + 1;
35492                 
35493                 if(!this.zoomable()){
35494                     break;
35495                 }
35496                 
35497                 if(
35498                         Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
35499                         Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
35500                 ){
35501                     continue;
35502                 }
35503                 
35504                 this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
35505
35506                 this.draw();
35507                 
35508                 return;
35509             }
35510             
35511             this.scale = this.startScale;
35512             
35513             this.onRotateFail();
35514             
35515             return false;
35516         }
35517         
35518         this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
35519
35520         if(this.isDocument){
35521             this.setThumbBoxSize();
35522             this.setThumbBoxPosition();
35523             this.setCanvasPosition();
35524         }
35525         
35526         this.draw();
35527         
35528         this.fireEvent('rotate', this, 'right');
35529     },
35530     
35531     onRotateFail : function()
35532     {
35533         this.errorEl.show(true);
35534         
35535         var _this = this;
35536         
35537         (function() { _this.errorEl.hide(true); }).defer(this.errorTimeout);
35538     },
35539     
35540     draw : function()
35541     {
35542         this.previewEl.dom.innerHTML = '';
35543         
35544         var canvasEl = document.createElement("canvas");
35545         
35546         var contextEl = canvasEl.getContext("2d");
35547         
35548         canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
35549         canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
35550         var center = this.imageEl.OriginWidth / 2;
35551         
35552         if(this.imageEl.OriginWidth < this.imageEl.OriginHeight){
35553             canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
35554             canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
35555             center = this.imageEl.OriginHeight / 2;
35556         }
35557         
35558         contextEl.scale(this.getScaleLevel(), this.getScaleLevel());
35559         
35560         contextEl.translate(center, center);
35561         contextEl.rotate(this.rotate * Math.PI / 180);
35562
35563         contextEl.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
35564         
35565         this.canvasEl = document.createElement("canvas");
35566         
35567         this.contextEl = this.canvasEl.getContext("2d");
35568         
35569         switch (this.rotate) {
35570             case 0 :
35571                 
35572                 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
35573                 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
35574                 
35575                 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
35576                 
35577                 break;
35578             case 90 : 
35579                 
35580                 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
35581                 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
35582                 
35583                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
35584                     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);
35585                     break;
35586                 }
35587                 
35588                 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
35589                 
35590                 break;
35591             case 180 :
35592                 
35593                 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
35594                 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
35595                 
35596                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
35597                     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);
35598                     break;
35599                 }
35600                 
35601                 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);
35602                 
35603                 break;
35604             case 270 :
35605                 
35606                 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
35607                 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
35608         
35609                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
35610                     this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
35611                     break;
35612                 }
35613                 
35614                 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);
35615                 
35616                 break;
35617             default : 
35618                 break;
35619         }
35620         
35621         this.previewEl.appendChild(this.canvasEl);
35622         
35623         this.setCanvasPosition();
35624     },
35625     
35626     crop : function()
35627     {
35628         if(!this.canvasLoaded){
35629             return;
35630         }
35631         
35632         var imageCanvas = document.createElement("canvas");
35633         
35634         var imageContext = imageCanvas.getContext("2d");
35635         
35636         imageCanvas.width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
35637         imageCanvas.height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
35638         
35639         var center = imageCanvas.width / 2;
35640         
35641         imageContext.translate(center, center);
35642         
35643         imageContext.rotate(this.rotate * Math.PI / 180);
35644         
35645         imageContext.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
35646         
35647         var canvas = document.createElement("canvas");
35648         
35649         var context = canvas.getContext("2d");
35650                 
35651         canvas.width = this.minWidth;
35652         canvas.height = this.minHeight;
35653
35654         switch (this.rotate) {
35655             case 0 :
35656                 
35657                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
35658                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
35659                 
35660                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
35661                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
35662                 
35663                 var targetWidth = this.minWidth - 2 * x;
35664                 var targetHeight = this.minHeight - 2 * y;
35665                 
35666                 var scale = 1;
35667                 
35668                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
35669                     scale = targetWidth / width;
35670                 }
35671                 
35672                 if(x > 0 && y == 0){
35673                     scale = targetHeight / height;
35674                 }
35675                 
35676                 if(x > 0 && y > 0){
35677                     scale = targetWidth / width;
35678                     
35679                     if(width < height){
35680                         scale = targetHeight / height;
35681                     }
35682                 }
35683                 
35684                 context.scale(scale, scale);
35685                 
35686                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
35687                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
35688
35689                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
35690                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
35691
35692                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
35693                 
35694                 break;
35695             case 90 : 
35696                 
35697                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
35698                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
35699                 
35700                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
35701                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
35702                 
35703                 var targetWidth = this.minWidth - 2 * x;
35704                 var targetHeight = this.minHeight - 2 * y;
35705                 
35706                 var scale = 1;
35707                 
35708                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
35709                     scale = targetWidth / width;
35710                 }
35711                 
35712                 if(x > 0 && y == 0){
35713                     scale = targetHeight / height;
35714                 }
35715                 
35716                 if(x > 0 && y > 0){
35717                     scale = targetWidth / width;
35718                     
35719                     if(width < height){
35720                         scale = targetHeight / height;
35721                     }
35722                 }
35723                 
35724                 context.scale(scale, scale);
35725                 
35726                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
35727                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
35728
35729                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
35730                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
35731                 
35732                 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
35733                 
35734                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
35735                 
35736                 break;
35737             case 180 :
35738                 
35739                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
35740                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
35741                 
35742                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
35743                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
35744                 
35745                 var targetWidth = this.minWidth - 2 * x;
35746                 var targetHeight = this.minHeight - 2 * y;
35747                 
35748                 var scale = 1;
35749                 
35750                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
35751                     scale = targetWidth / width;
35752                 }
35753                 
35754                 if(x > 0 && y == 0){
35755                     scale = targetHeight / height;
35756                 }
35757                 
35758                 if(x > 0 && y > 0){
35759                     scale = targetWidth / width;
35760                     
35761                     if(width < height){
35762                         scale = targetHeight / height;
35763                     }
35764                 }
35765                 
35766                 context.scale(scale, scale);
35767                 
35768                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
35769                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
35770
35771                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
35772                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
35773
35774                 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
35775                 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
35776                 
35777                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
35778                 
35779                 break;
35780             case 270 :
35781                 
35782                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
35783                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
35784                 
35785                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
35786                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
35787                 
35788                 var targetWidth = this.minWidth - 2 * x;
35789                 var targetHeight = this.minHeight - 2 * y;
35790                 
35791                 var scale = 1;
35792                 
35793                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
35794                     scale = targetWidth / width;
35795                 }
35796                 
35797                 if(x > 0 && y == 0){
35798                     scale = targetHeight / height;
35799                 }
35800                 
35801                 if(x > 0 && y > 0){
35802                     scale = targetWidth / width;
35803                     
35804                     if(width < height){
35805                         scale = targetHeight / height;
35806                     }
35807                 }
35808                 
35809                 context.scale(scale, scale);
35810                 
35811                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
35812                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
35813
35814                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
35815                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
35816                 
35817                 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
35818                 
35819                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
35820                 
35821                 break;
35822             default : 
35823                 break;
35824         }
35825         
35826         this.cropData = canvas.toDataURL(this.cropType);
35827         
35828         if(this.fireEvent('crop', this, this.cropData) !== false){
35829             this.process(this.file, this.cropData);
35830         }
35831         
35832         return;
35833         
35834     },
35835     
35836     setThumbBoxSize : function()
35837     {
35838         var width, height;
35839         
35840         if(this.isDocument && typeof(this.imageEl) != 'undefined'){
35841             width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.max(this.minWidth, this.minHeight) : Math.min(this.minWidth, this.minHeight);
35842             height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.min(this.minWidth, this.minHeight) : Math.max(this.minWidth, this.minHeight);
35843             
35844             this.minWidth = width;
35845             this.minHeight = height;
35846             
35847             if(this.rotate == 90 || this.rotate == 270){
35848                 this.minWidth = height;
35849                 this.minHeight = width;
35850             }
35851         }
35852         
35853         height = 300;
35854         width = Math.ceil(this.minWidth * height / this.minHeight);
35855         
35856         if(this.minWidth > this.minHeight){
35857             width = 300;
35858             height = Math.ceil(this.minHeight * width / this.minWidth);
35859         }
35860         
35861         this.thumbEl.setStyle({
35862             width : width + 'px',
35863             height : height + 'px'
35864         });
35865
35866         return;
35867             
35868     },
35869     
35870     setThumbBoxPosition : function()
35871     {
35872         var x = Math.ceil((this.bodyEl.getWidth() - this.thumbEl.getWidth()) / 2 );
35873         var y = Math.ceil((this.bodyEl.getHeight() - this.thumbEl.getHeight()) / 2);
35874         
35875         this.thumbEl.setLeft(x);
35876         this.thumbEl.setTop(y);
35877         
35878     },
35879     
35880     baseRotateLevel : function()
35881     {
35882         this.baseRotate = 1;
35883         
35884         if(
35885                 typeof(this.exif) != 'undefined' &&
35886                 typeof(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != 'undefined' &&
35887                 [1, 3, 6, 8].indexOf(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != -1
35888         ){
35889             this.baseRotate = this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']];
35890         }
35891         
35892         this.rotate = Roo.bootstrap.UploadCropbox['Orientation'][this.baseRotate];
35893         
35894     },
35895     
35896     baseScaleLevel : function()
35897     {
35898         var width, height;
35899         
35900         if(this.isDocument){
35901             
35902             if(this.baseRotate == 6 || this.baseRotate == 8){
35903             
35904                 height = this.thumbEl.getHeight();
35905                 this.baseScale = height / this.imageEl.OriginWidth;
35906
35907                 if(this.imageEl.OriginHeight * this.baseScale > this.thumbEl.getWidth()){
35908                     width = this.thumbEl.getWidth();
35909                     this.baseScale = width / this.imageEl.OriginHeight;
35910                 }
35911
35912                 return;
35913             }
35914
35915             height = this.thumbEl.getHeight();
35916             this.baseScale = height / this.imageEl.OriginHeight;
35917
35918             if(this.imageEl.OriginWidth * this.baseScale > this.thumbEl.getWidth()){
35919                 width = this.thumbEl.getWidth();
35920                 this.baseScale = width / this.imageEl.OriginWidth;
35921             }
35922
35923             return;
35924         }
35925         
35926         if(this.baseRotate == 6 || this.baseRotate == 8){
35927             
35928             width = this.thumbEl.getHeight();
35929             this.baseScale = width / this.imageEl.OriginHeight;
35930             
35931             if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getWidth()){
35932                 height = this.thumbEl.getWidth();
35933                 this.baseScale = height / this.imageEl.OriginHeight;
35934             }
35935             
35936             if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
35937                 height = this.thumbEl.getWidth();
35938                 this.baseScale = height / this.imageEl.OriginHeight;
35939                 
35940                 if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getHeight()){
35941                     width = this.thumbEl.getHeight();
35942                     this.baseScale = width / this.imageEl.OriginWidth;
35943                 }
35944             }
35945             
35946             return;
35947         }
35948         
35949         width = this.thumbEl.getWidth();
35950         this.baseScale = width / this.imageEl.OriginWidth;
35951         
35952         if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getHeight()){
35953             height = this.thumbEl.getHeight();
35954             this.baseScale = height / this.imageEl.OriginHeight;
35955         }
35956         
35957         if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
35958             
35959             height = this.thumbEl.getHeight();
35960             this.baseScale = height / this.imageEl.OriginHeight;
35961             
35962             if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getWidth()){
35963                 width = this.thumbEl.getWidth();
35964                 this.baseScale = width / this.imageEl.OriginWidth;
35965             }
35966             
35967         }
35968         
35969         return;
35970     },
35971     
35972     getScaleLevel : function()
35973     {
35974         return this.baseScale * Math.pow(1.1, this.scale);
35975     },
35976     
35977     onTouchStart : function(e)
35978     {
35979         if(!this.canvasLoaded){
35980             this.beforeSelectFile(e);
35981             return;
35982         }
35983         
35984         var touches = e.browserEvent.touches;
35985         
35986         if(!touches){
35987             return;
35988         }
35989         
35990         if(touches.length == 1){
35991             this.onMouseDown(e);
35992             return;
35993         }
35994         
35995         if(touches.length != 2){
35996             return;
35997         }
35998         
35999         var coords = [];
36000         
36001         for(var i = 0, finger; finger = touches[i]; i++){
36002             coords.push(finger.pageX, finger.pageY);
36003         }
36004         
36005         var x = Math.pow(coords[0] - coords[2], 2);
36006         var y = Math.pow(coords[1] - coords[3], 2);
36007         
36008         this.startDistance = Math.sqrt(x + y);
36009         
36010         this.startScale = this.scale;
36011         
36012         this.pinching = true;
36013         this.dragable = false;
36014         
36015     },
36016     
36017     onTouchMove : function(e)
36018     {
36019         if(!this.pinching && !this.dragable){
36020             return;
36021         }
36022         
36023         var touches = e.browserEvent.touches;
36024         
36025         if(!touches){
36026             return;
36027         }
36028         
36029         if(this.dragable){
36030             this.onMouseMove(e);
36031             return;
36032         }
36033         
36034         var coords = [];
36035         
36036         for(var i = 0, finger; finger = touches[i]; i++){
36037             coords.push(finger.pageX, finger.pageY);
36038         }
36039         
36040         var x = Math.pow(coords[0] - coords[2], 2);
36041         var y = Math.pow(coords[1] - coords[3], 2);
36042         
36043         this.endDistance = Math.sqrt(x + y);
36044         
36045         this.scale = this.startScale + Math.floor(Math.log(this.endDistance / this.startDistance) / Math.log(1.1));
36046         
36047         if(!this.zoomable()){
36048             this.scale = this.startScale;
36049             return;
36050         }
36051         
36052         this.draw();
36053         
36054     },
36055     
36056     onTouchEnd : function(e)
36057     {
36058         this.pinching = false;
36059         this.dragable = false;
36060         
36061     },
36062     
36063     process : function(file, crop)
36064     {
36065         if(this.loadMask){
36066             this.maskEl.mask(this.loadingText);
36067         }
36068         
36069         this.xhr = new XMLHttpRequest();
36070         
36071         file.xhr = this.xhr;
36072
36073         this.xhr.open(this.method, this.url, true);
36074         
36075         var headers = {
36076             "Accept": "application/json",
36077             "Cache-Control": "no-cache",
36078             "X-Requested-With": "XMLHttpRequest"
36079         };
36080         
36081         for (var headerName in headers) {
36082             var headerValue = headers[headerName];
36083             if (headerValue) {
36084                 this.xhr.setRequestHeader(headerName, headerValue);
36085             }
36086         }
36087         
36088         var _this = this;
36089         
36090         this.xhr.onload = function()
36091         {
36092             _this.xhrOnLoad(_this.xhr);
36093         }
36094         
36095         this.xhr.onerror = function()
36096         {
36097             _this.xhrOnError(_this.xhr);
36098         }
36099         
36100         var formData = new FormData();
36101
36102         formData.append('returnHTML', 'NO');
36103         
36104         if(crop){
36105             formData.append('crop', crop);
36106         }
36107         
36108         if(typeof(file) != 'undefined' && (typeof(file.id) == 'undefined' || file.id * 1 < 1)){
36109             formData.append(this.paramName, file, file.name);
36110         }
36111         
36112         if(typeof(file.filename) != 'undefined'){
36113             formData.append('filename', file.filename);
36114         }
36115         
36116         if(typeof(file.mimetype) != 'undefined'){
36117             formData.append('mimetype', file.mimetype);
36118         }
36119         
36120         if(this.fireEvent('arrange', this, formData) != false){
36121             this.xhr.send(formData);
36122         };
36123     },
36124     
36125     xhrOnLoad : function(xhr)
36126     {
36127         if(this.loadMask){
36128             this.maskEl.unmask();
36129         }
36130         
36131         if (xhr.readyState !== 4) {
36132             this.fireEvent('exception', this, xhr);
36133             return;
36134         }
36135
36136         var response = Roo.decode(xhr.responseText);
36137         
36138         if(!response.success){
36139             this.fireEvent('exception', this, xhr);
36140             return;
36141         }
36142         
36143         var response = Roo.decode(xhr.responseText);
36144         
36145         this.fireEvent('upload', this, response);
36146         
36147     },
36148     
36149     xhrOnError : function()
36150     {
36151         if(this.loadMask){
36152             this.maskEl.unmask();
36153         }
36154         
36155         Roo.log('xhr on error');
36156         
36157         var response = Roo.decode(xhr.responseText);
36158           
36159         Roo.log(response);
36160         
36161     },
36162     
36163     prepare : function(file)
36164     {   
36165         if(this.loadMask){
36166             this.maskEl.mask(this.loadingText);
36167         }
36168         
36169         this.file = false;
36170         this.exif = {};
36171         
36172         if(typeof(file) === 'string'){
36173             this.loadCanvas(file);
36174             return;
36175         }
36176         
36177         if(!file || !this.urlAPI){
36178             return;
36179         }
36180         
36181         this.file = file;
36182         this.cropType = file.type;
36183         
36184         var _this = this;
36185         
36186         if(this.fireEvent('prepare', this, this.file) != false){
36187             
36188             var reader = new FileReader();
36189             
36190             reader.onload = function (e) {
36191                 if (e.target.error) {
36192                     Roo.log(e.target.error);
36193                     return;
36194                 }
36195                 
36196                 var buffer = e.target.result,
36197                     dataView = new DataView(buffer),
36198                     offset = 2,
36199                     maxOffset = dataView.byteLength - 4,
36200                     markerBytes,
36201                     markerLength;
36202                 
36203                 if (dataView.getUint16(0) === 0xffd8) {
36204                     while (offset < maxOffset) {
36205                         markerBytes = dataView.getUint16(offset);
36206                         
36207                         if ((markerBytes >= 0xffe0 && markerBytes <= 0xffef) || markerBytes === 0xfffe) {
36208                             markerLength = dataView.getUint16(offset + 2) + 2;
36209                             if (offset + markerLength > dataView.byteLength) {
36210                                 Roo.log('Invalid meta data: Invalid segment size.');
36211                                 break;
36212                             }
36213                             
36214                             if(markerBytes == 0xffe1){
36215                                 _this.parseExifData(
36216                                     dataView,
36217                                     offset,
36218                                     markerLength
36219                                 );
36220                             }
36221                             
36222                             offset += markerLength;
36223                             
36224                             continue;
36225                         }
36226                         
36227                         break;
36228                     }
36229                     
36230                 }
36231                 
36232                 var url = _this.urlAPI.createObjectURL(_this.file);
36233                 
36234                 _this.loadCanvas(url);
36235                 
36236                 return;
36237             }
36238             
36239             reader.readAsArrayBuffer(this.file);
36240             
36241         }
36242         
36243     },
36244     
36245     parseExifData : function(dataView, offset, length)
36246     {
36247         var tiffOffset = offset + 10,
36248             littleEndian,
36249             dirOffset;
36250     
36251         if (dataView.getUint32(offset + 4) !== 0x45786966) {
36252             // No Exif data, might be XMP data instead
36253             return;
36254         }
36255         
36256         // Check for the ASCII code for "Exif" (0x45786966):
36257         if (dataView.getUint32(offset + 4) !== 0x45786966) {
36258             // No Exif data, might be XMP data instead
36259             return;
36260         }
36261         if (tiffOffset + 8 > dataView.byteLength) {
36262             Roo.log('Invalid Exif data: Invalid segment size.');
36263             return;
36264         }
36265         // Check for the two null bytes:
36266         if (dataView.getUint16(offset + 8) !== 0x0000) {
36267             Roo.log('Invalid Exif data: Missing byte alignment offset.');
36268             return;
36269         }
36270         // Check the byte alignment:
36271         switch (dataView.getUint16(tiffOffset)) {
36272         case 0x4949:
36273             littleEndian = true;
36274             break;
36275         case 0x4D4D:
36276             littleEndian = false;
36277             break;
36278         default:
36279             Roo.log('Invalid Exif data: Invalid byte alignment marker.');
36280             return;
36281         }
36282         // Check for the TIFF tag marker (0x002A):
36283         if (dataView.getUint16(tiffOffset + 2, littleEndian) !== 0x002A) {
36284             Roo.log('Invalid Exif data: Missing TIFF marker.');
36285             return;
36286         }
36287         // Retrieve the directory offset bytes, usually 0x00000008 or 8 decimal:
36288         dirOffset = dataView.getUint32(tiffOffset + 4, littleEndian);
36289         
36290         this.parseExifTags(
36291             dataView,
36292             tiffOffset,
36293             tiffOffset + dirOffset,
36294             littleEndian
36295         );
36296     },
36297     
36298     parseExifTags : function(dataView, tiffOffset, dirOffset, littleEndian)
36299     {
36300         var tagsNumber,
36301             dirEndOffset,
36302             i;
36303         if (dirOffset + 6 > dataView.byteLength) {
36304             Roo.log('Invalid Exif data: Invalid directory offset.');
36305             return;
36306         }
36307         tagsNumber = dataView.getUint16(dirOffset, littleEndian);
36308         dirEndOffset = dirOffset + 2 + 12 * tagsNumber;
36309         if (dirEndOffset + 4 > dataView.byteLength) {
36310             Roo.log('Invalid Exif data: Invalid directory size.');
36311             return;
36312         }
36313         for (i = 0; i < tagsNumber; i += 1) {
36314             this.parseExifTag(
36315                 dataView,
36316                 tiffOffset,
36317                 dirOffset + 2 + 12 * i, // tag offset
36318                 littleEndian
36319             );
36320         }
36321         // Return the offset to the next directory:
36322         return dataView.getUint32(dirEndOffset, littleEndian);
36323     },
36324     
36325     parseExifTag : function (dataView, tiffOffset, offset, littleEndian) 
36326     {
36327         var tag = dataView.getUint16(offset, littleEndian);
36328         
36329         this.exif[tag] = this.getExifValue(
36330             dataView,
36331             tiffOffset,
36332             offset,
36333             dataView.getUint16(offset + 2, littleEndian), // tag type
36334             dataView.getUint32(offset + 4, littleEndian), // tag length
36335             littleEndian
36336         );
36337     },
36338     
36339     getExifValue : function (dataView, tiffOffset, offset, type, length, littleEndian)
36340     {
36341         var tagType = Roo.bootstrap.UploadCropbox.exifTagTypes[type],
36342             tagSize,
36343             dataOffset,
36344             values,
36345             i,
36346             str,
36347             c;
36348     
36349         if (!tagType) {
36350             Roo.log('Invalid Exif data: Invalid tag type.');
36351             return;
36352         }
36353         
36354         tagSize = tagType.size * length;
36355         // Determine if the value is contained in the dataOffset bytes,
36356         // or if the value at the dataOffset is a pointer to the actual data:
36357         dataOffset = tagSize > 4 ?
36358                 tiffOffset + dataView.getUint32(offset + 8, littleEndian) : (offset + 8);
36359         if (dataOffset + tagSize > dataView.byteLength) {
36360             Roo.log('Invalid Exif data: Invalid data offset.');
36361             return;
36362         }
36363         if (length === 1) {
36364             return tagType.getValue(dataView, dataOffset, littleEndian);
36365         }
36366         values = [];
36367         for (i = 0; i < length; i += 1) {
36368             values[i] = tagType.getValue(dataView, dataOffset + i * tagType.size, littleEndian);
36369         }
36370         
36371         if (tagType.ascii) {
36372             str = '';
36373             // Concatenate the chars:
36374             for (i = 0; i < values.length; i += 1) {
36375                 c = values[i];
36376                 // Ignore the terminating NULL byte(s):
36377                 if (c === '\u0000') {
36378                     break;
36379                 }
36380                 str += c;
36381             }
36382             return str;
36383         }
36384         return values;
36385     }
36386     
36387 });
36388
36389 Roo.apply(Roo.bootstrap.UploadCropbox, {
36390     tags : {
36391         'Orientation': 0x0112
36392     },
36393     
36394     Orientation: {
36395             1: 0, //'top-left',
36396 //            2: 'top-right',
36397             3: 180, //'bottom-right',
36398 //            4: 'bottom-left',
36399 //            5: 'left-top',
36400             6: 90, //'right-top',
36401 //            7: 'right-bottom',
36402             8: 270 //'left-bottom'
36403     },
36404     
36405     exifTagTypes : {
36406         // byte, 8-bit unsigned int:
36407         1: {
36408             getValue: function (dataView, dataOffset) {
36409                 return dataView.getUint8(dataOffset);
36410             },
36411             size: 1
36412         },
36413         // ascii, 8-bit byte:
36414         2: {
36415             getValue: function (dataView, dataOffset) {
36416                 return String.fromCharCode(dataView.getUint8(dataOffset));
36417             },
36418             size: 1,
36419             ascii: true
36420         },
36421         // short, 16 bit int:
36422         3: {
36423             getValue: function (dataView, dataOffset, littleEndian) {
36424                 return dataView.getUint16(dataOffset, littleEndian);
36425             },
36426             size: 2
36427         },
36428         // long, 32 bit int:
36429         4: {
36430             getValue: function (dataView, dataOffset, littleEndian) {
36431                 return dataView.getUint32(dataOffset, littleEndian);
36432             },
36433             size: 4
36434         },
36435         // rational = two long values, first is numerator, second is denominator:
36436         5: {
36437             getValue: function (dataView, dataOffset, littleEndian) {
36438                 return dataView.getUint32(dataOffset, littleEndian) /
36439                     dataView.getUint32(dataOffset + 4, littleEndian);
36440             },
36441             size: 8
36442         },
36443         // slong, 32 bit signed int:
36444         9: {
36445             getValue: function (dataView, dataOffset, littleEndian) {
36446                 return dataView.getInt32(dataOffset, littleEndian);
36447             },
36448             size: 4
36449         },
36450         // srational, two slongs, first is numerator, second is denominator:
36451         10: {
36452             getValue: function (dataView, dataOffset, littleEndian) {
36453                 return dataView.getInt32(dataOffset, littleEndian) /
36454                     dataView.getInt32(dataOffset + 4, littleEndian);
36455             },
36456             size: 8
36457         }
36458     },
36459     
36460     footer : {
36461         STANDARD : [
36462             {
36463                 tag : 'div',
36464                 cls : 'btn-group roo-upload-cropbox-rotate-left',
36465                 action : 'rotate-left',
36466                 cn : [
36467                     {
36468                         tag : 'button',
36469                         cls : 'btn btn-default',
36470                         html : '<i class="fa fa-undo"></i>'
36471                     }
36472                 ]
36473             },
36474             {
36475                 tag : 'div',
36476                 cls : 'btn-group roo-upload-cropbox-picture',
36477                 action : 'picture',
36478                 cn : [
36479                     {
36480                         tag : 'button',
36481                         cls : 'btn btn-default',
36482                         html : '<i class="fa fa-picture-o"></i>'
36483                     }
36484                 ]
36485             },
36486             {
36487                 tag : 'div',
36488                 cls : 'btn-group roo-upload-cropbox-rotate-right',
36489                 action : 'rotate-right',
36490                 cn : [
36491                     {
36492                         tag : 'button',
36493                         cls : 'btn btn-default',
36494                         html : '<i class="fa fa-repeat"></i>'
36495                     }
36496                 ]
36497             }
36498         ],
36499         DOCUMENT : [
36500             {
36501                 tag : 'div',
36502                 cls : 'btn-group roo-upload-cropbox-rotate-left',
36503                 action : 'rotate-left',
36504                 cn : [
36505                     {
36506                         tag : 'button',
36507                         cls : 'btn btn-default',
36508                         html : '<i class="fa fa-undo"></i>'
36509                     }
36510                 ]
36511             },
36512             {
36513                 tag : 'div',
36514                 cls : 'btn-group roo-upload-cropbox-download',
36515                 action : 'download',
36516                 cn : [
36517                     {
36518                         tag : 'button',
36519                         cls : 'btn btn-default',
36520                         html : '<i class="fa fa-download"></i>'
36521                     }
36522                 ]
36523             },
36524             {
36525                 tag : 'div',
36526                 cls : 'btn-group roo-upload-cropbox-crop',
36527                 action : 'crop',
36528                 cn : [
36529                     {
36530                         tag : 'button',
36531                         cls : 'btn btn-default',
36532                         html : '<i class="fa fa-crop"></i>'
36533                     }
36534                 ]
36535             },
36536             {
36537                 tag : 'div',
36538                 cls : 'btn-group roo-upload-cropbox-trash',
36539                 action : 'trash',
36540                 cn : [
36541                     {
36542                         tag : 'button',
36543                         cls : 'btn btn-default',
36544                         html : '<i class="fa fa-trash"></i>'
36545                     }
36546                 ]
36547             },
36548             {
36549                 tag : 'div',
36550                 cls : 'btn-group roo-upload-cropbox-rotate-right',
36551                 action : 'rotate-right',
36552                 cn : [
36553                     {
36554                         tag : 'button',
36555                         cls : 'btn btn-default',
36556                         html : '<i class="fa fa-repeat"></i>'
36557                     }
36558                 ]
36559             }
36560         ],
36561         ROTATOR : [
36562             {
36563                 tag : 'div',
36564                 cls : 'btn-group roo-upload-cropbox-rotate-left',
36565                 action : 'rotate-left',
36566                 cn : [
36567                     {
36568                         tag : 'button',
36569                         cls : 'btn btn-default',
36570                         html : '<i class="fa fa-undo"></i>'
36571                     }
36572                 ]
36573             },
36574             {
36575                 tag : 'div',
36576                 cls : 'btn-group roo-upload-cropbox-rotate-right',
36577                 action : 'rotate-right',
36578                 cn : [
36579                     {
36580                         tag : 'button',
36581                         cls : 'btn btn-default',
36582                         html : '<i class="fa fa-repeat"></i>'
36583                     }
36584                 ]
36585             }
36586         ]
36587     }
36588 });
36589
36590 /*
36591 * Licence: LGPL
36592 */
36593
36594 /**
36595  * @class Roo.bootstrap.DocumentManager
36596  * @extends Roo.bootstrap.Component
36597  * Bootstrap DocumentManager class
36598  * @cfg {String} paramName default 'imageUpload'
36599  * @cfg {String} toolTipName default 'filename'
36600  * @cfg {String} method default POST
36601  * @cfg {String} url action url
36602  * @cfg {Number} boxes number of boxes, 0 is no limit.. default 0
36603  * @cfg {Boolean} multiple multiple upload default true
36604  * @cfg {Number} thumbSize default 300
36605  * @cfg {String} fieldLabel
36606  * @cfg {Number} labelWidth default 4
36607  * @cfg {String} labelAlign (left|top) default left
36608  * @cfg {Boolean} editable (true|false) allow edit when upload a image default true
36609 * @cfg {Number} labellg set the width of label (1-12)
36610  * @cfg {Number} labelmd set the width of label (1-12)
36611  * @cfg {Number} labelsm set the width of label (1-12)
36612  * @cfg {Number} labelxs set the width of label (1-12)
36613  * 
36614  * @constructor
36615  * Create a new DocumentManager
36616  * @param {Object} config The config object
36617  */
36618
36619 Roo.bootstrap.DocumentManager = function(config){
36620     Roo.bootstrap.DocumentManager.superclass.constructor.call(this, config);
36621     
36622     this.files = [];
36623     this.delegates = [];
36624     
36625     this.addEvents({
36626         /**
36627          * @event initial
36628          * Fire when initial the DocumentManager
36629          * @param {Roo.bootstrap.DocumentManager} this
36630          */
36631         "initial" : true,
36632         /**
36633          * @event inspect
36634          * inspect selected file
36635          * @param {Roo.bootstrap.DocumentManager} this
36636          * @param {File} file
36637          */
36638         "inspect" : true,
36639         /**
36640          * @event exception
36641          * Fire when xhr load exception
36642          * @param {Roo.bootstrap.DocumentManager} this
36643          * @param {XMLHttpRequest} xhr
36644          */
36645         "exception" : true,
36646         /**
36647          * @event afterupload
36648          * Fire when xhr load exception
36649          * @param {Roo.bootstrap.DocumentManager} this
36650          * @param {XMLHttpRequest} xhr
36651          */
36652         "afterupload" : true,
36653         /**
36654          * @event prepare
36655          * prepare the form data
36656          * @param {Roo.bootstrap.DocumentManager} this
36657          * @param {Object} formData
36658          */
36659         "prepare" : true,
36660         /**
36661          * @event remove
36662          * Fire when remove the file
36663          * @param {Roo.bootstrap.DocumentManager} this
36664          * @param {Object} file
36665          */
36666         "remove" : true,
36667         /**
36668          * @event refresh
36669          * Fire after refresh the file
36670          * @param {Roo.bootstrap.DocumentManager} this
36671          */
36672         "refresh" : true,
36673         /**
36674          * @event click
36675          * Fire after click the image
36676          * @param {Roo.bootstrap.DocumentManager} this
36677          * @param {Object} file
36678          */
36679         "click" : true,
36680         /**
36681          * @event edit
36682          * Fire when upload a image and editable set to true
36683          * @param {Roo.bootstrap.DocumentManager} this
36684          * @param {Object} file
36685          */
36686         "edit" : true,
36687         /**
36688          * @event beforeselectfile
36689          * Fire before select file
36690          * @param {Roo.bootstrap.DocumentManager} this
36691          */
36692         "beforeselectfile" : true,
36693         /**
36694          * @event process
36695          * Fire before process file
36696          * @param {Roo.bootstrap.DocumentManager} this
36697          * @param {Object} file
36698          */
36699         "process" : true,
36700         /**
36701          * @event previewrendered
36702          * Fire when preview rendered
36703          * @param {Roo.bootstrap.DocumentManager} this
36704          * @param {Object} file
36705          */
36706         "previewrendered" : true,
36707         /**
36708          */
36709         "previewResize" : true
36710         
36711     });
36712 };
36713
36714 Roo.extend(Roo.bootstrap.DocumentManager, Roo.bootstrap.Component,  {
36715     
36716     boxes : 0,
36717     inputName : '',
36718     thumbSize : 300,
36719     multiple : true,
36720     files : false,
36721     method : 'POST',
36722     url : '',
36723     paramName : 'imageUpload',
36724     toolTipName : 'filename',
36725     fieldLabel : '',
36726     labelWidth : 4,
36727     labelAlign : 'left',
36728     editable : true,
36729     delegates : false,
36730     xhr : false, 
36731     
36732     labellg : 0,
36733     labelmd : 0,
36734     labelsm : 0,
36735     labelxs : 0,
36736     
36737     getAutoCreate : function()
36738     {   
36739         var managerWidget = {
36740             tag : 'div',
36741             cls : 'roo-document-manager',
36742             cn : [
36743                 {
36744                     tag : 'input',
36745                     cls : 'roo-document-manager-selector',
36746                     type : 'file'
36747                 },
36748                 {
36749                     tag : 'div',
36750                     cls : 'roo-document-manager-uploader',
36751                     cn : [
36752                         {
36753                             tag : 'div',
36754                             cls : 'roo-document-manager-upload-btn',
36755                             html : '<i class="fa fa-plus"></i>'
36756                         }
36757                     ]
36758                     
36759                 }
36760             ]
36761         };
36762         
36763         var content = [
36764             {
36765                 tag : 'div',
36766                 cls : 'column col-md-12',
36767                 cn : managerWidget
36768             }
36769         ];
36770         
36771         if(this.fieldLabel.length){
36772             
36773             content = [
36774                 {
36775                     tag : 'div',
36776                     cls : 'column col-md-12',
36777                     html : this.fieldLabel
36778                 },
36779                 {
36780                     tag : 'div',
36781                     cls : 'column col-md-12',
36782                     cn : managerWidget
36783                 }
36784             ];
36785
36786             if(this.labelAlign == 'left'){
36787                 content = [
36788                     {
36789                         tag : 'div',
36790                         cls : 'column',
36791                         html : this.fieldLabel
36792                     },
36793                     {
36794                         tag : 'div',
36795                         cls : 'column',
36796                         cn : managerWidget
36797                     }
36798                 ];
36799                 
36800                 if(this.labelWidth > 12){
36801                     content[0].style = "width: " + this.labelWidth + 'px';
36802                 }
36803
36804                 if(this.labelWidth < 13 && this.labelmd == 0){
36805                     this.labelmd = this.labelWidth;
36806                 }
36807
36808                 if(this.labellg > 0){
36809                     content[0].cls += ' col-lg-' + this.labellg;
36810                     content[1].cls += ' col-lg-' + (12 - this.labellg);
36811                 }
36812
36813                 if(this.labelmd > 0){
36814                     content[0].cls += ' col-md-' + this.labelmd;
36815                     content[1].cls += ' col-md-' + (12 - this.labelmd);
36816                 }
36817
36818                 if(this.labelsm > 0){
36819                     content[0].cls += ' col-sm-' + this.labelsm;
36820                     content[1].cls += ' col-sm-' + (12 - this.labelsm);
36821                 }
36822
36823                 if(this.labelxs > 0){
36824                     content[0].cls += ' col-xs-' + this.labelxs;
36825                     content[1].cls += ' col-xs-' + (12 - this.labelxs);
36826                 }
36827                 
36828             }
36829         }
36830         
36831         var cfg = {
36832             tag : 'div',
36833             cls : 'row clearfix',
36834             cn : content
36835         };
36836         
36837         return cfg;
36838         
36839     },
36840     
36841     initEvents : function()
36842     {
36843         this.managerEl = this.el.select('.roo-document-manager', true).first();
36844         this.managerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
36845         
36846         this.selectorEl = this.el.select('.roo-document-manager-selector', true).first();
36847         this.selectorEl.hide();
36848         
36849         if(this.multiple){
36850             this.selectorEl.attr('multiple', 'multiple');
36851         }
36852         
36853         this.selectorEl.on('change', this.onFileSelected, this);
36854         
36855         this.uploader = this.el.select('.roo-document-manager-uploader', true).first();
36856         this.uploader.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
36857         
36858         this.uploader.on('click', this.onUploaderClick, this);
36859         
36860         this.renderProgressDialog();
36861         
36862         var _this = this;
36863         
36864         window.addEventListener("resize", function() { _this.refresh(); } );
36865         
36866         this.fireEvent('initial', this);
36867     },
36868     
36869     renderProgressDialog : function()
36870     {
36871         var _this = this;
36872         
36873         this.progressDialog = new Roo.bootstrap.Modal({
36874             cls : 'roo-document-manager-progress-dialog',
36875             allow_close : false,
36876             animate : false,
36877             title : '',
36878             buttons : [
36879                 {
36880                     name  :'cancel',
36881                     weight : 'danger',
36882                     html : 'Cancel'
36883                 }
36884             ], 
36885             listeners : { 
36886                 btnclick : function() {
36887                     _this.uploadCancel();
36888                     this.hide();
36889                 }
36890             }
36891         });
36892          
36893         this.progressDialog.render(Roo.get(document.body));
36894          
36895         this.progress = new Roo.bootstrap.Progress({
36896             cls : 'roo-document-manager-progress',
36897             active : true,
36898             striped : true
36899         });
36900         
36901         this.progress.render(this.progressDialog.getChildContainer());
36902         
36903         this.progressBar = new Roo.bootstrap.ProgressBar({
36904             cls : 'roo-document-manager-progress-bar',
36905             aria_valuenow : 0,
36906             aria_valuemin : 0,
36907             aria_valuemax : 12,
36908             panel : 'success'
36909         });
36910         
36911         this.progressBar.render(this.progress.getChildContainer());
36912     },
36913     
36914     onUploaderClick : function(e)
36915     {
36916         e.preventDefault();
36917      
36918         if(this.fireEvent('beforeselectfile', this) != false){
36919             this.selectorEl.dom.click();
36920         }
36921         
36922     },
36923     
36924     onFileSelected : function(e)
36925     {
36926         e.preventDefault();
36927         
36928         if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
36929             return;
36930         }
36931         
36932         Roo.each(this.selectorEl.dom.files, function(file){
36933             if(this.fireEvent('inspect', this, file) != false){
36934                 this.files.push(file);
36935             }
36936         }, this);
36937         
36938         this.queue();
36939         
36940     },
36941     
36942     queue : function()
36943     {
36944         this.selectorEl.dom.value = '';
36945         
36946         if(!this.files || !this.files.length){
36947             return;
36948         }
36949         
36950         if(this.boxes > 0 && this.files.length > this.boxes){
36951             this.files = this.files.slice(0, this.boxes);
36952         }
36953         
36954         this.uploader.show();
36955         
36956         if(this.boxes > 0 && this.files.length > this.boxes - 1){
36957             this.uploader.hide();
36958         }
36959         
36960         var _this = this;
36961         
36962         var files = [];
36963         
36964         var docs = [];
36965         
36966         Roo.each(this.files, function(file){
36967             
36968             if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
36969                 var f = this.renderPreview(file);
36970                 files.push(f);
36971                 return;
36972             }
36973             
36974             if(file.type.indexOf('image') != -1){
36975                 this.delegates.push(
36976                     (function(){
36977                         _this.process(file);
36978                     }).createDelegate(this)
36979                 );
36980         
36981                 return;
36982             }
36983             
36984             docs.push(
36985                 (function(){
36986                     _this.process(file);
36987                 }).createDelegate(this)
36988             );
36989             
36990         }, this);
36991         
36992         this.files = files;
36993         
36994         this.delegates = this.delegates.concat(docs);
36995         
36996         if(!this.delegates.length){
36997             this.refresh();
36998             return;
36999         }
37000         
37001         this.progressBar.aria_valuemax = this.delegates.length;
37002         
37003         this.arrange();
37004         
37005         return;
37006     },
37007     
37008     arrange : function()
37009     {
37010         if(!this.delegates.length){
37011             this.progressDialog.hide();
37012             this.refresh();
37013             return;
37014         }
37015         
37016         var delegate = this.delegates.shift();
37017         
37018         this.progressDialog.show();
37019         
37020         this.progressDialog.setTitle((this.progressBar.aria_valuemax - this.delegates.length) + ' / ' + this.progressBar.aria_valuemax);
37021         
37022         this.progressBar.update(this.progressBar.aria_valuemax - this.delegates.length);
37023         
37024         delegate();
37025     },
37026     
37027     refresh : function()
37028     {
37029         this.uploader.show();
37030         
37031         if(this.boxes > 0 && this.files.length > this.boxes - 1){
37032             this.uploader.hide();
37033         }
37034         
37035         Roo.isTouch ? this.closable(false) : this.closable(true);
37036         
37037         this.fireEvent('refresh', this);
37038     },
37039     
37040     onRemove : function(e, el, o)
37041     {
37042         e.preventDefault();
37043         
37044         this.fireEvent('remove', this, o);
37045         
37046     },
37047     
37048     remove : function(o)
37049     {
37050         var files = [];
37051         
37052         Roo.each(this.files, function(file){
37053             if(typeof(file.id) == 'undefined' || file.id * 1 < 1 || file.id != o.id){
37054                 files.push(file);
37055                 return;
37056             }
37057
37058             o.target.remove();
37059
37060         }, this);
37061         
37062         this.files = files;
37063         
37064         this.refresh();
37065     },
37066     
37067     clear : function()
37068     {
37069         Roo.each(this.files, function(file){
37070             if(!file.target){
37071                 return;
37072             }
37073             
37074             file.target.remove();
37075
37076         }, this);
37077         
37078         this.files = [];
37079         
37080         this.refresh();
37081     },
37082     
37083     onClick : function(e, el, o)
37084     {
37085         e.preventDefault();
37086         
37087         this.fireEvent('click', this, o);
37088         
37089     },
37090     
37091     closable : function(closable)
37092     {
37093         Roo.each(this.managerEl.select('.roo-document-manager-preview > button.close', true).elements, function(el){
37094             
37095             el.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
37096             
37097             if(closable){
37098                 el.show();
37099                 return;
37100             }
37101             
37102             el.hide();
37103             
37104         }, this);
37105     },
37106     
37107     xhrOnLoad : function(xhr)
37108     {
37109         Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
37110             el.remove();
37111         }, this);
37112         
37113         if (xhr.readyState !== 4) {
37114             this.arrange();
37115             this.fireEvent('exception', this, xhr);
37116             return;
37117         }
37118
37119         var response = Roo.decode(xhr.responseText);
37120         
37121         if(!response.success){
37122             this.arrange();
37123             this.fireEvent('exception', this, xhr);
37124             return;
37125         }
37126         
37127         var file = this.renderPreview(response.data);
37128         
37129         this.files.push(file);
37130         
37131         this.arrange();
37132         
37133         this.fireEvent('afterupload', this, xhr);
37134         
37135     },
37136     
37137     xhrOnError : function(xhr)
37138     {
37139         Roo.log('xhr on error');
37140         
37141         var response = Roo.decode(xhr.responseText);
37142           
37143         Roo.log(response);
37144         
37145         this.arrange();
37146     },
37147     
37148     process : function(file)
37149     {
37150         if(this.fireEvent('process', this, file) !== false){
37151             if(this.editable && file.type.indexOf('image') != -1){
37152                 this.fireEvent('edit', this, file);
37153                 return;
37154             }
37155
37156             this.uploadStart(file, false);
37157
37158             return;
37159         }
37160         
37161     },
37162     
37163     uploadStart : function(file, crop)
37164     {
37165         this.xhr = new XMLHttpRequest();
37166         
37167         if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
37168             this.arrange();
37169             return;
37170         }
37171         
37172         file.xhr = this.xhr;
37173             
37174         this.managerEl.createChild({
37175             tag : 'div',
37176             cls : 'roo-document-manager-loading',
37177             cn : [
37178                 {
37179                     tag : 'div',
37180                     tooltip : file.name,
37181                     cls : 'roo-document-manager-thumb',
37182                     html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
37183                 }
37184             ]
37185
37186         });
37187
37188         this.xhr.open(this.method, this.url, true);
37189         
37190         var headers = {
37191             "Accept": "application/json",
37192             "Cache-Control": "no-cache",
37193             "X-Requested-With": "XMLHttpRequest"
37194         };
37195         
37196         for (var headerName in headers) {
37197             var headerValue = headers[headerName];
37198             if (headerValue) {
37199                 this.xhr.setRequestHeader(headerName, headerValue);
37200             }
37201         }
37202         
37203         var _this = this;
37204         
37205         this.xhr.onload = function()
37206         {
37207             _this.xhrOnLoad(_this.xhr);
37208         }
37209         
37210         this.xhr.onerror = function()
37211         {
37212             _this.xhrOnError(_this.xhr);
37213         }
37214         
37215         var formData = new FormData();
37216
37217         formData.append('returnHTML', 'NO');
37218         
37219         if(crop){
37220             formData.append('crop', crop);
37221         }
37222         
37223         formData.append(this.paramName, file, file.name);
37224         
37225         var options = {
37226             file : file, 
37227             manually : false
37228         };
37229         
37230         if(this.fireEvent('prepare', this, formData, options) != false){
37231             
37232             if(options.manually){
37233                 return;
37234             }
37235             
37236             this.xhr.send(formData);
37237             return;
37238         };
37239         
37240         this.uploadCancel();
37241     },
37242     
37243     uploadCancel : function()
37244     {
37245         if (this.xhr) {
37246             this.xhr.abort();
37247         }
37248         
37249         this.delegates = [];
37250         
37251         Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
37252             el.remove();
37253         }, this);
37254         
37255         this.arrange();
37256     },
37257     
37258     renderPreview : function(file)
37259     {
37260         if(typeof(file.target) != 'undefined' && file.target){
37261             return file;
37262         }
37263         
37264         var img_src = encodeURI(baseURL +'/Images/Thumb/' + this.thumbSize + '/' + file.id + '/' + file.filename);
37265         
37266         var previewEl = this.managerEl.createChild({
37267             tag : 'div',
37268             cls : 'roo-document-manager-preview',
37269             cn : [
37270                 {
37271                     tag : 'div',
37272                     tooltip : file[this.toolTipName],
37273                     cls : 'roo-document-manager-thumb',
37274                     html : '<img tooltip="' + file[this.toolTipName] + '" src="' + img_src + '">'
37275                 },
37276                 {
37277                     tag : 'button',
37278                     cls : 'close',
37279                     html : '<i class="fa fa-times-circle"></i>'
37280                 }
37281             ]
37282         });
37283
37284         var close = previewEl.select('button.close', true).first();
37285
37286         close.on('click', this.onRemove, this, file);
37287
37288         file.target = previewEl;
37289
37290         var image = previewEl.select('img', true).first();
37291         
37292         var _this = this;
37293         
37294         image.dom.addEventListener("load", function(){ _this.onPreviewLoad(file, image); });
37295         
37296         image.on('click', this.onClick, this, file);
37297         
37298         this.fireEvent('previewrendered', this, file);
37299         
37300         return file;
37301         
37302     },
37303     
37304     onPreviewLoad : function(file, image)
37305     {
37306         if(typeof(file.target) == 'undefined' || !file.target){
37307             return;
37308         }
37309         
37310         var width = image.dom.naturalWidth || image.dom.width;
37311         var height = image.dom.naturalHeight || image.dom.height;
37312         
37313         if(!this.previewResize) {
37314             return;
37315         }
37316         
37317         if(width > height){
37318             file.target.addClass('wide');
37319             return;
37320         }
37321         
37322         file.target.addClass('tall');
37323         return;
37324         
37325     },
37326     
37327     uploadFromSource : function(file, crop)
37328     {
37329         this.xhr = new XMLHttpRequest();
37330         
37331         this.managerEl.createChild({
37332             tag : 'div',
37333             cls : 'roo-document-manager-loading',
37334             cn : [
37335                 {
37336                     tag : 'div',
37337                     tooltip : file.name,
37338                     cls : 'roo-document-manager-thumb',
37339                     html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
37340                 }
37341             ]
37342
37343         });
37344
37345         this.xhr.open(this.method, this.url, true);
37346         
37347         var headers = {
37348             "Accept": "application/json",
37349             "Cache-Control": "no-cache",
37350             "X-Requested-With": "XMLHttpRequest"
37351         };
37352         
37353         for (var headerName in headers) {
37354             var headerValue = headers[headerName];
37355             if (headerValue) {
37356                 this.xhr.setRequestHeader(headerName, headerValue);
37357             }
37358         }
37359         
37360         var _this = this;
37361         
37362         this.xhr.onload = function()
37363         {
37364             _this.xhrOnLoad(_this.xhr);
37365         }
37366         
37367         this.xhr.onerror = function()
37368         {
37369             _this.xhrOnError(_this.xhr);
37370         }
37371         
37372         var formData = new FormData();
37373
37374         formData.append('returnHTML', 'NO');
37375         
37376         formData.append('crop', crop);
37377         
37378         if(typeof(file.filename) != 'undefined'){
37379             formData.append('filename', file.filename);
37380         }
37381         
37382         if(typeof(file.mimetype) != 'undefined'){
37383             formData.append('mimetype', file.mimetype);
37384         }
37385         
37386         Roo.log(formData);
37387         
37388         if(this.fireEvent('prepare', this, formData) != false){
37389             this.xhr.send(formData);
37390         };
37391     }
37392 });
37393
37394 /*
37395 * Licence: LGPL
37396 */
37397
37398 /**
37399  * @class Roo.bootstrap.DocumentViewer
37400  * @extends Roo.bootstrap.Component
37401  * Bootstrap DocumentViewer class
37402  * @cfg {Boolean} showDownload (true|false) show download button (default true)
37403  * @cfg {Boolean} showTrash (true|false) show trash button (default true)
37404  * 
37405  * @constructor
37406  * Create a new DocumentViewer
37407  * @param {Object} config The config object
37408  */
37409
37410 Roo.bootstrap.DocumentViewer = function(config){
37411     Roo.bootstrap.DocumentViewer.superclass.constructor.call(this, config);
37412     
37413     this.addEvents({
37414         /**
37415          * @event initial
37416          * Fire after initEvent
37417          * @param {Roo.bootstrap.DocumentViewer} this
37418          */
37419         "initial" : true,
37420         /**
37421          * @event click
37422          * Fire after click
37423          * @param {Roo.bootstrap.DocumentViewer} this
37424          */
37425         "click" : true,
37426         /**
37427          * @event download
37428          * Fire after download button
37429          * @param {Roo.bootstrap.DocumentViewer} this
37430          */
37431         "download" : true,
37432         /**
37433          * @event trash
37434          * Fire after trash button
37435          * @param {Roo.bootstrap.DocumentViewer} this
37436          */
37437         "trash" : true
37438         
37439     });
37440 };
37441
37442 Roo.extend(Roo.bootstrap.DocumentViewer, Roo.bootstrap.Component,  {
37443     
37444     showDownload : true,
37445     
37446     showTrash : true,
37447     
37448     getAutoCreate : function()
37449     {
37450         var cfg = {
37451             tag : 'div',
37452             cls : 'roo-document-viewer',
37453             cn : [
37454                 {
37455                     tag : 'div',
37456                     cls : 'roo-document-viewer-body',
37457                     cn : [
37458                         {
37459                             tag : 'div',
37460                             cls : 'roo-document-viewer-thumb',
37461                             cn : [
37462                                 {
37463                                     tag : 'img',
37464                                     cls : 'roo-document-viewer-image'
37465                                 }
37466                             ]
37467                         }
37468                     ]
37469                 },
37470                 {
37471                     tag : 'div',
37472                     cls : 'roo-document-viewer-footer',
37473                     cn : {
37474                         tag : 'div',
37475                         cls : 'btn-group btn-group-justified roo-document-viewer-btn-group',
37476                         cn : [
37477                             {
37478                                 tag : 'div',
37479                                 cls : 'btn-group roo-document-viewer-download',
37480                                 cn : [
37481                                     {
37482                                         tag : 'button',
37483                                         cls : 'btn btn-default',
37484                                         html : '<i class="fa fa-download"></i>'
37485                                     }
37486                                 ]
37487                             },
37488                             {
37489                                 tag : 'div',
37490                                 cls : 'btn-group roo-document-viewer-trash',
37491                                 cn : [
37492                                     {
37493                                         tag : 'button',
37494                                         cls : 'btn btn-default',
37495                                         html : '<i class="fa fa-trash"></i>'
37496                                     }
37497                                 ]
37498                             }
37499                         ]
37500                     }
37501                 }
37502             ]
37503         };
37504         
37505         return cfg;
37506     },
37507     
37508     initEvents : function()
37509     {
37510         this.bodyEl = this.el.select('.roo-document-viewer-body', true).first();
37511         this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY);
37512         
37513         this.thumbEl = this.el.select('.roo-document-viewer-thumb', true).first();
37514         this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY);
37515         
37516         this.imageEl = this.el.select('.roo-document-viewer-image', true).first();
37517         this.imageEl.setVisibilityMode(Roo.Element.DISPLAY);
37518         
37519         this.footerEl = this.el.select('.roo-document-viewer-footer', true).first();
37520         this.footerEl.setVisibilityMode(Roo.Element.DISPLAY);
37521         
37522         this.downloadBtn = this.el.select('.roo-document-viewer-download', true).first();
37523         this.downloadBtn.setVisibilityMode(Roo.Element.DISPLAY);
37524         
37525         this.trashBtn = this.el.select('.roo-document-viewer-trash', true).first();
37526         this.trashBtn.setVisibilityMode(Roo.Element.DISPLAY);
37527         
37528         this.bodyEl.on('click', this.onClick, this);
37529         this.downloadBtn.on('click', this.onDownload, this);
37530         this.trashBtn.on('click', this.onTrash, this);
37531         
37532         this.downloadBtn.hide();
37533         this.trashBtn.hide();
37534         
37535         if(this.showDownload){
37536             this.downloadBtn.show();
37537         }
37538         
37539         if(this.showTrash){
37540             this.trashBtn.show();
37541         }
37542         
37543         if(!this.showDownload && !this.showTrash) {
37544             this.footerEl.hide();
37545         }
37546         
37547     },
37548     
37549     initial : function()
37550     {
37551         this.fireEvent('initial', this);
37552         
37553     },
37554     
37555     onClick : function(e)
37556     {
37557         e.preventDefault();
37558         
37559         this.fireEvent('click', this);
37560     },
37561     
37562     onDownload : function(e)
37563     {
37564         e.preventDefault();
37565         
37566         this.fireEvent('download', this);
37567     },
37568     
37569     onTrash : function(e)
37570     {
37571         e.preventDefault();
37572         
37573         this.fireEvent('trash', this);
37574     }
37575     
37576 });
37577 /*
37578  * - LGPL
37579  *
37580  * FieldLabel
37581  * 
37582  */
37583
37584 /**
37585  * @class Roo.bootstrap.form.FieldLabel
37586  * @extends Roo.bootstrap.Component
37587  * Bootstrap FieldLabel class
37588  * @cfg {String} html contents of the element
37589  * @cfg {String} tag tag of the element default label
37590  * @cfg {String} cls class of the element
37591  * @cfg {String} target label target 
37592  * @cfg {Boolean} allowBlank (true|false) target allowBlank default true
37593  * @cfg {String} invalidClass DEPRICATED - BS4 uses is-invalid
37594  * @cfg {String} validClass DEPRICATED - BS4 uses is-valid
37595  * @cfg {String} iconTooltip default "This field is required"
37596  * @cfg {String} indicatorpos (left|right) default left
37597  * 
37598  * @constructor
37599  * Create a new FieldLabel
37600  * @param {Object} config The config object
37601  */
37602
37603 Roo.bootstrap.form.FieldLabel = function(config){
37604     Roo.bootstrap.Element.superclass.constructor.call(this, config);
37605     
37606     this.addEvents({
37607             /**
37608              * @event invalid
37609              * Fires after the field has been marked as invalid.
37610              * @param {Roo.form.FieldLabel} this
37611              * @param {String} msg The validation message
37612              */
37613             invalid : true,
37614             /**
37615              * @event valid
37616              * Fires after the field has been validated with no errors.
37617              * @param {Roo.form.FieldLabel} this
37618              */
37619             valid : true
37620         });
37621 };
37622
37623 Roo.extend(Roo.bootstrap.form.FieldLabel, Roo.bootstrap.Component,  {
37624     
37625     tag: 'label',
37626     cls: '',
37627     html: '',
37628     target: '',
37629     allowBlank : true,
37630     invalidClass : 'has-warning',
37631     validClass : 'has-success',
37632     iconTooltip : 'This field is required',
37633     indicatorpos : 'left',
37634     
37635     getAutoCreate : function(){
37636         
37637         var cls = "";
37638         if (!this.allowBlank) {
37639             cls  = "visible";
37640         }
37641         
37642         var cfg = {
37643             tag : this.tag,
37644             cls : 'roo-bootstrap-field-label ' + this.cls,
37645             for : this.target,
37646             cn : [
37647                 {
37648                     tag : 'i',
37649                     cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star ' + cls,
37650                     tooltip : this.iconTooltip
37651                 },
37652                 {
37653                     tag : 'span',
37654                     html : this.html
37655                 }
37656             ] 
37657         };
37658         
37659         if(this.indicatorpos == 'right'){
37660             var cfg = {
37661                 tag : this.tag,
37662                 cls : 'roo-bootstrap-field-label ' + this.cls,
37663                 for : this.target,
37664                 cn : [
37665                     {
37666                         tag : 'span',
37667                         html : this.html
37668                     },
37669                     {
37670                         tag : 'i',
37671                         cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star '+ cls,
37672                         tooltip : this.iconTooltip
37673                     }
37674                 ] 
37675             };
37676         }
37677         
37678         return cfg;
37679     },
37680     
37681     initEvents: function() 
37682     {
37683         Roo.bootstrap.Element.superclass.initEvents.call(this);
37684         
37685         this.indicator = this.indicatorEl();
37686         
37687         if(this.indicator){
37688             this.indicator.removeClass('visible');
37689             this.indicator.addClass('invisible');
37690         }
37691         
37692         Roo.bootstrap.form.FieldLabel.register(this);
37693     },
37694     
37695     indicatorEl : function()
37696     {
37697         var indicator = this.el.select('i.roo-required-indicator',true).first();
37698         
37699         if(!indicator){
37700             return false;
37701         }
37702         
37703         return indicator;
37704         
37705     },
37706     
37707     /**
37708      * Mark this field as valid
37709      */
37710     markValid : function()
37711     {
37712         if(this.indicator){
37713             this.indicator.removeClass('visible');
37714             this.indicator.addClass('invisible');
37715         }
37716         if (Roo.bootstrap.version == 3) {
37717             this.el.removeClass(this.invalidClass);
37718             this.el.addClass(this.validClass);
37719         } else {
37720             this.el.removeClass('is-invalid');
37721             this.el.addClass('is-valid');
37722         }
37723         
37724         
37725         this.fireEvent('valid', this);
37726     },
37727     
37728     /**
37729      * Mark this field as invalid
37730      * @param {String} msg The validation message
37731      */
37732     markInvalid : function(msg)
37733     {
37734         if(this.indicator){
37735             this.indicator.removeClass('invisible');
37736             this.indicator.addClass('visible');
37737         }
37738           if (Roo.bootstrap.version == 3) {
37739             this.el.removeClass(this.validClass);
37740             this.el.addClass(this.invalidClass);
37741         } else {
37742             this.el.removeClass('is-valid');
37743             this.el.addClass('is-invalid');
37744         }
37745         
37746         
37747         this.fireEvent('invalid', this, msg);
37748     }
37749     
37750    
37751 });
37752
37753 Roo.apply(Roo.bootstrap.form.FieldLabel, {
37754     
37755     groups: {},
37756     
37757      /**
37758     * register a FieldLabel Group
37759     * @param {Roo.bootstrap.form.FieldLabel} the FieldLabel to add
37760     */
37761     register : function(label)
37762     {
37763         if(this.groups.hasOwnProperty(label.target)){
37764             return;
37765         }
37766      
37767         this.groups[label.target] = label;
37768         
37769     },
37770     /**
37771     * fetch a FieldLabel Group based on the target
37772     * @param {string} target
37773     * @returns {Roo.bootstrap.form.FieldLabel} the CheckBox group
37774     */
37775     get: function(target) {
37776         if (typeof(this.groups[target]) == 'undefined') {
37777             return false;
37778         }
37779         
37780         return this.groups[target] ;
37781     }
37782 });
37783
37784  
37785
37786  /*
37787  * - LGPL
37788  *
37789  * page DateSplitField.
37790  * 
37791  */
37792
37793
37794 /**
37795  * @class Roo.bootstrap.form.DateSplitField
37796  * @extends Roo.bootstrap.Component
37797  * Bootstrap DateSplitField class
37798  * @cfg {string} fieldLabel - the label associated
37799  * @cfg {Number} labelWidth set the width of label (0-12)
37800  * @cfg {String} labelAlign (top|left)
37801  * @cfg {Boolean} dayAllowBlank (true|false) default false
37802  * @cfg {Boolean} monthAllowBlank (true|false) default false
37803  * @cfg {Boolean} yearAllowBlank (true|false) default false
37804  * @cfg {string} dayPlaceholder 
37805  * @cfg {string} monthPlaceholder
37806  * @cfg {string} yearPlaceholder
37807  * @cfg {string} dayFormat default 'd'
37808  * @cfg {string} monthFormat default 'm'
37809  * @cfg {string} yearFormat default 'Y'
37810  * @cfg {Number} labellg set the width of label (1-12)
37811  * @cfg {Number} labelmd set the width of label (1-12)
37812  * @cfg {Number} labelsm set the width of label (1-12)
37813  * @cfg {Number} labelxs set the width of label (1-12)
37814
37815  *     
37816  * @constructor
37817  * Create a new DateSplitField
37818  * @param {Object} config The config object
37819  */
37820
37821 Roo.bootstrap.form.DateSplitField = function(config){
37822     Roo.bootstrap.form.DateSplitField.superclass.constructor.call(this, config);
37823     
37824     this.addEvents({
37825         // raw events
37826          /**
37827          * @event years
37828          * getting the data of years
37829          * @param {Roo.bootstrap.form.DateSplitField} this
37830          * @param {Object} years
37831          */
37832         "years" : true,
37833         /**
37834          * @event days
37835          * getting the data of days
37836          * @param {Roo.bootstrap.form.DateSplitField} this
37837          * @param {Object} days
37838          */
37839         "days" : true,
37840         /**
37841          * @event invalid
37842          * Fires after the field has been marked as invalid.
37843          * @param {Roo.form.Field} this
37844          * @param {String} msg The validation message
37845          */
37846         invalid : true,
37847        /**
37848          * @event valid
37849          * Fires after the field has been validated with no errors.
37850          * @param {Roo.form.Field} this
37851          */
37852         valid : true
37853     });
37854 };
37855
37856 Roo.extend(Roo.bootstrap.form.DateSplitField, Roo.bootstrap.Component,  {
37857     
37858     fieldLabel : '',
37859     labelAlign : 'top',
37860     labelWidth : 3,
37861     dayAllowBlank : false,
37862     monthAllowBlank : false,
37863     yearAllowBlank : false,
37864     dayPlaceholder : '',
37865     monthPlaceholder : '',
37866     yearPlaceholder : '',
37867     dayFormat : 'd',
37868     monthFormat : 'm',
37869     yearFormat : 'Y',
37870     isFormField : true,
37871     labellg : 0,
37872     labelmd : 0,
37873     labelsm : 0,
37874     labelxs : 0,
37875     
37876     getAutoCreate : function()
37877     {
37878         var cfg = {
37879             tag : 'div',
37880             cls : 'row roo-date-split-field-group',
37881             cn : [
37882                 {
37883                     tag : 'input',
37884                     type : 'hidden',
37885                     cls : 'form-hidden-field roo-date-split-field-group-value',
37886                     name : this.name
37887                 }
37888             ]
37889         };
37890         
37891         var labelCls = 'col-md-12';
37892         var contentCls = 'col-md-4';
37893         
37894         if(this.fieldLabel){
37895             
37896             var label = {
37897                 tag : 'div',
37898                 cls : 'column roo-date-split-field-label col-md-' + ((this.labelAlign == 'top') ? '12' : this.labelWidth),
37899                 cn : [
37900                     {
37901                         tag : 'label',
37902                         html : this.fieldLabel
37903                     }
37904                 ]
37905             };
37906             
37907             if(this.labelAlign == 'left'){
37908             
37909                 if(this.labelWidth > 12){
37910                     label.style = "width: " + this.labelWidth + 'px';
37911                 }
37912
37913                 if(this.labelWidth < 13 && this.labelmd == 0){
37914                     this.labelmd = this.labelWidth;
37915                 }
37916
37917                 if(this.labellg > 0){
37918                     labelCls = ' col-lg-' + this.labellg;
37919                     contentCls = ' col-lg-' + ((12 - this.labellg) / 3);
37920                 }
37921
37922                 if(this.labelmd > 0){
37923                     labelCls = ' col-md-' + this.labelmd;
37924                     contentCls = ' col-md-' + ((12 - this.labelmd) / 3);
37925                 }
37926
37927                 if(this.labelsm > 0){
37928                     labelCls = ' col-sm-' + this.labelsm;
37929                     contentCls = ' col-sm-' + ((12 - this.labelsm) / 3);
37930                 }
37931
37932                 if(this.labelxs > 0){
37933                     labelCls = ' col-xs-' + this.labelxs;
37934                     contentCls = ' col-xs-' + ((12 - this.labelxs) / 3);
37935                 }
37936             }
37937             
37938             label.cls += ' ' + labelCls;
37939             
37940             cfg.cn.push(label);
37941         }
37942         
37943         Roo.each(['day', 'month', 'year'], function(t){
37944             cfg.cn.push({
37945                 tag : 'div',
37946                 cls : 'column roo-date-split-field-' + t + ' ' + contentCls
37947             });
37948         }, this);
37949         
37950         return cfg;
37951     },
37952     
37953     inputEl: function ()
37954     {
37955         return this.el.select('.roo-date-split-field-group-value', true).first();
37956     },
37957     
37958     onRender : function(ct, position) 
37959     {
37960         var _this = this;
37961         
37962         Roo.bootstrap.DateSplitFiel.superclass.onRender.call(this, ct, position);
37963         
37964         this.inputEl = this.el.select('.roo-date-split-field-group-value', true).first();
37965         
37966         this.dayField = new Roo.bootstrap.form.ComboBox({
37967             allowBlank : this.dayAllowBlank,
37968             alwaysQuery : true,
37969             displayField : 'value',
37970             editable : false,
37971             fieldLabel : '',
37972             forceSelection : true,
37973             mode : 'local',
37974             placeholder : this.dayPlaceholder,
37975             selectOnFocus : true,
37976             tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
37977             triggerAction : 'all',
37978             typeAhead : true,
37979             valueField : 'value',
37980             store : new Roo.data.SimpleStore({
37981                 data : (function() {    
37982                     var days = [];
37983                     _this.fireEvent('days', _this, days);
37984                     return days;
37985                 })(),
37986                 fields : [ 'value' ]
37987             }),
37988             listeners : {
37989                 select : function (_self, record, index)
37990                 {
37991                     _this.setValue(_this.getValue());
37992                 }
37993             }
37994         });
37995
37996         this.dayField.render(this.el.select('.roo-date-split-field-day', true).first(), null);
37997         
37998         this.monthField = new Roo.bootstrap.form.MonthField({
37999             after : '<i class=\"fa fa-calendar\"></i>',
38000             allowBlank : this.monthAllowBlank,
38001             placeholder : this.monthPlaceholder,
38002             readOnly : true,
38003             listeners : {
38004                 render : function (_self)
38005                 {
38006                     this.el.select('span.input-group-addon', true).first().on('click', function(e){
38007                         e.preventDefault();
38008                         _self.focus();
38009                     });
38010                 },
38011                 select : function (_self, oldvalue, newvalue)
38012                 {
38013                     _this.setValue(_this.getValue());
38014                 }
38015             }
38016         });
38017         
38018         this.monthField.render(this.el.select('.roo-date-split-field-month', true).first(), null);
38019         
38020         this.yearField = new Roo.bootstrap.form.ComboBox({
38021             allowBlank : this.yearAllowBlank,
38022             alwaysQuery : true,
38023             displayField : 'value',
38024             editable : false,
38025             fieldLabel : '',
38026             forceSelection : true,
38027             mode : 'local',
38028             placeholder : this.yearPlaceholder,
38029             selectOnFocus : true,
38030             tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
38031             triggerAction : 'all',
38032             typeAhead : true,
38033             valueField : 'value',
38034             store : new Roo.data.SimpleStore({
38035                 data : (function() {
38036                     var years = [];
38037                     _this.fireEvent('years', _this, years);
38038                     return years;
38039                 })(),
38040                 fields : [ 'value' ]
38041             }),
38042             listeners : {
38043                 select : function (_self, record, index)
38044                 {
38045                     _this.setValue(_this.getValue());
38046                 }
38047             }
38048         });
38049
38050         this.yearField.render(this.el.select('.roo-date-split-field-year', true).first(), null);
38051     },
38052     
38053     setValue : function(v, format)
38054     {
38055         this.inputEl.dom.value = v;
38056         
38057         var f = format || (this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat);
38058         
38059         var d = Date.parseDate(v, f);
38060         
38061         if(!d){
38062             this.validate();
38063             return;
38064         }
38065         
38066         this.setDay(d.format(this.dayFormat));
38067         this.setMonth(d.format(this.monthFormat));
38068         this.setYear(d.format(this.yearFormat));
38069         
38070         this.validate();
38071         
38072         return;
38073     },
38074     
38075     setDay : function(v)
38076     {
38077         this.dayField.setValue(v);
38078         this.inputEl.dom.value = this.getValue();
38079         this.validate();
38080         return;
38081     },
38082     
38083     setMonth : function(v)
38084     {
38085         this.monthField.setValue(v, true);
38086         this.inputEl.dom.value = this.getValue();
38087         this.validate();
38088         return;
38089     },
38090     
38091     setYear : function(v)
38092     {
38093         this.yearField.setValue(v);
38094         this.inputEl.dom.value = this.getValue();
38095         this.validate();
38096         return;
38097     },
38098     
38099     getDay : function()
38100     {
38101         return this.dayField.getValue();
38102     },
38103     
38104     getMonth : function()
38105     {
38106         return this.monthField.getValue();
38107     },
38108     
38109     getYear : function()
38110     {
38111         return this.yearField.getValue();
38112     },
38113     
38114     getValue : function()
38115     {
38116         var f = this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat;
38117         
38118         var date = this.yearField.getValue() + '-' + this.monthField.getValue() + '-' + this.dayField.getValue();
38119         
38120         return date;
38121     },
38122     
38123     reset : function()
38124     {
38125         this.setDay('');
38126         this.setMonth('');
38127         this.setYear('');
38128         this.inputEl.dom.value = '';
38129         this.validate();
38130         return;
38131     },
38132     
38133     validate : function()
38134     {
38135         var d = this.dayField.validate();
38136         var m = this.monthField.validate();
38137         var y = this.yearField.validate();
38138         
38139         var valid = true;
38140         
38141         if(
38142                 (!this.dayAllowBlank && !d) ||
38143                 (!this.monthAllowBlank && !m) ||
38144                 (!this.yearAllowBlank && !y)
38145         ){
38146             valid = false;
38147         }
38148         
38149         if(this.dayAllowBlank && this.monthAllowBlank && this.yearAllowBlank){
38150             return valid;
38151         }
38152         
38153         if(valid){
38154             this.markValid();
38155             return valid;
38156         }
38157         
38158         this.markInvalid();
38159         
38160         return valid;
38161     },
38162     
38163     markValid : function()
38164     {
38165         
38166         var label = this.el.select('label', true).first();
38167         var icon = this.el.select('i.fa-star', true).first();
38168
38169         if(label && icon){
38170             icon.remove();
38171         }
38172         
38173         this.fireEvent('valid', this);
38174     },
38175     
38176      /**
38177      * Mark this field as invalid
38178      * @param {String} msg The validation message
38179      */
38180     markInvalid : function(msg)
38181     {
38182         
38183         var label = this.el.select('label', true).first();
38184         var icon = this.el.select('i.fa-star', true).first();
38185
38186         if(label && !icon){
38187             this.el.select('.roo-date-split-field-label', true).createChild({
38188                 tag : 'i',
38189                 cls : 'text-danger fa fa-lg fa-star',
38190                 tooltip : 'This field is required',
38191                 style : 'margin-right:5px;'
38192             }, label, true);
38193         }
38194         
38195         this.fireEvent('invalid', this, msg);
38196     },
38197     
38198     clearInvalid : function()
38199     {
38200         var label = this.el.select('label', true).first();
38201         var icon = this.el.select('i.fa-star', true).first();
38202
38203         if(label && icon){
38204             icon.remove();
38205         }
38206         
38207         this.fireEvent('valid', this);
38208     },
38209     
38210     getName: function()
38211     {
38212         return this.name;
38213     }
38214     
38215 });
38216
38217  
38218
38219 /**
38220  * @class Roo.bootstrap.LayoutMasonry
38221  * @extends Roo.bootstrap.Component
38222  * @children Roo.bootstrap.Element Roo.bootstrap.Img Roo.bootstrap.MasonryBrick
38223  * Bootstrap Layout Masonry class
38224  *
38225  * This is based on 
38226  * http://masonry.desandro.com
38227  *
38228  * The idea is to render all the bricks based on vertical width...
38229  *
38230  * The original code extends 'outlayer' - we might need to use that....
38231
38232  * @constructor
38233  * Create a new Element
38234  * @param {Object} config The config object
38235  */
38236
38237 Roo.bootstrap.LayoutMasonry = function(config){
38238     
38239     Roo.bootstrap.LayoutMasonry.superclass.constructor.call(this, config);
38240     
38241     this.bricks = [];
38242     
38243     Roo.bootstrap.LayoutMasonry.register(this);
38244     
38245     this.addEvents({
38246         // raw events
38247         /**
38248          * @event layout
38249          * Fire after layout the items
38250          * @param {Roo.bootstrap.LayoutMasonry} this
38251          * @param {Roo.EventObject} e
38252          */
38253         "layout" : true
38254     });
38255     
38256 };
38257
38258 Roo.extend(Roo.bootstrap.LayoutMasonry, Roo.bootstrap.Component,  {
38259     
38260     /**
38261      * @cfg {Boolean} isLayoutInstant = no animation?
38262      */   
38263     isLayoutInstant : false, // needed?
38264    
38265     /**
38266      * @cfg {Number} boxWidth  width of the columns
38267      */   
38268     boxWidth : 450,
38269     
38270       /**
38271      * @cfg {Number} boxHeight  - 0 for square, or fix it at a certian height
38272      */   
38273     boxHeight : 0,
38274     
38275     /**
38276      * @cfg {Number} padWidth padding below box..
38277      */   
38278     padWidth : 10, 
38279     
38280     /**
38281      * @cfg {Number} gutter gutter width..
38282      */   
38283     gutter : 10,
38284     
38285      /**
38286      * @cfg {Number} maxCols maximum number of columns
38287      */   
38288     
38289     maxCols: 0,
38290     
38291     /**
38292      * @cfg {Boolean} isAutoInitial defalut true
38293      */   
38294     isAutoInitial : true, 
38295     
38296     containerWidth: 0,
38297     
38298     /**
38299      * @cfg {Boolean} isHorizontal defalut false
38300      */   
38301     isHorizontal : false, 
38302
38303     currentSize : null,
38304     
38305     tag: 'div',
38306     
38307     cls: '',
38308     
38309     bricks: null, //CompositeElement
38310     
38311     cols : 1,
38312     
38313     _isLayoutInited : false,
38314     
38315 //    isAlternative : false, // only use for vertical layout...
38316     
38317     /**
38318      * @cfg {Number} alternativePadWidth padding below box..
38319      */   
38320     alternativePadWidth : 50,
38321     
38322     selectedBrick : [],
38323     
38324     getAutoCreate : function(){
38325         
38326         var cfg = Roo.apply({}, Roo.bootstrap.LayoutMasonry.superclass.getAutoCreate.call(this));
38327         
38328         var cfg = {
38329             tag: this.tag,
38330             cls: 'blog-masonary-wrapper ' + this.cls,
38331             cn : {
38332                 cls : 'mas-boxes masonary'
38333             }
38334         };
38335         
38336         return cfg;
38337     },
38338     
38339     getChildContainer: function( )
38340     {
38341         if (this.boxesEl) {
38342             return this.boxesEl;
38343         }
38344         
38345         this.boxesEl = this.el.select('.mas-boxes').first();
38346         
38347         return this.boxesEl;
38348     },
38349     
38350     
38351     initEvents : function()
38352     {
38353         var _this = this;
38354         
38355         if(this.isAutoInitial){
38356             Roo.log('hook children rendered');
38357             this.on('childrenrendered', function() {
38358                 Roo.log('children rendered');
38359                 _this.initial();
38360             } ,this);
38361         }
38362     },
38363     
38364     initial : function()
38365     {
38366         this.selectedBrick = [];
38367         
38368         this.currentSize = this.el.getBox(true);
38369         
38370         Roo.EventManager.onWindowResize(this.resize, this); 
38371
38372         if(!this.isAutoInitial){
38373             this.layout();
38374             return;
38375         }
38376         
38377         this.layout();
38378         
38379         return;
38380         //this.layout.defer(500,this);
38381         
38382     },
38383     
38384     resize : function()
38385     {
38386         var cs = this.el.getBox(true);
38387         
38388         if (
38389                 this.currentSize.width == cs.width && 
38390                 this.currentSize.x == cs.x && 
38391                 this.currentSize.height == cs.height && 
38392                 this.currentSize.y == cs.y 
38393         ) {
38394             Roo.log("no change in with or X or Y");
38395             return;
38396         }
38397         
38398         this.currentSize = cs;
38399         
38400         this.layout();
38401         
38402     },
38403     
38404     layout : function()
38405     {   
38406         this._resetLayout();
38407         
38408         var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
38409         
38410         this.layoutItems( isInstant );
38411       
38412         this._isLayoutInited = true;
38413         
38414         this.fireEvent('layout', this);
38415         
38416     },
38417     
38418     _resetLayout : function()
38419     {
38420         if(this.isHorizontal){
38421             this.horizontalMeasureColumns();
38422             return;
38423         }
38424         
38425         this.verticalMeasureColumns();
38426         
38427     },
38428     
38429     verticalMeasureColumns : function()
38430     {
38431         this.getContainerWidth();
38432         
38433 //        if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
38434 //            this.colWidth = Math.floor(this.containerWidth * 0.8);
38435 //            return;
38436 //        }
38437         
38438         var boxWidth = this.boxWidth + this.padWidth;
38439         
38440         if(this.containerWidth < this.boxWidth){
38441             boxWidth = this.containerWidth
38442         }
38443         
38444         var containerWidth = this.containerWidth;
38445         
38446         var cols = Math.floor(containerWidth / boxWidth);
38447         
38448         this.cols = Math.max( cols, 1 );
38449         
38450         this.cols = this.maxCols > 0 ? Math.min( this.cols, this.maxCols ) : this.cols;
38451         
38452         var totalBoxWidth = this.cols * boxWidth - this.padWidth;
38453         
38454         var avail = Math.floor((containerWidth - totalBoxWidth) / this.cols);
38455         
38456         this.colWidth = boxWidth + avail - this.padWidth;
38457         
38458         this.unitWidth = Math.round((this.colWidth - (this.gutter * 2)) / 3);
38459         this.unitHeight = this.boxHeight > 0 ? this.boxHeight  : this.unitWidth;
38460     },
38461     
38462     horizontalMeasureColumns : function()
38463     {
38464         this.getContainerWidth();
38465         
38466         var boxWidth = this.boxWidth;
38467         
38468         if(this.containerWidth < boxWidth){
38469             boxWidth = this.containerWidth;
38470         }
38471         
38472         this.unitWidth = Math.floor((boxWidth - (this.gutter * 2)) / 3);
38473         
38474         this.el.setHeight(boxWidth);
38475         
38476     },
38477     
38478     getContainerWidth : function()
38479     {
38480         this.containerWidth = this.el.getBox(true).width;  //maybe use getComputedWidth
38481     },
38482     
38483     layoutItems : function( isInstant )
38484     {
38485         Roo.log(this.bricks);
38486         
38487         var items = Roo.apply([], this.bricks);
38488         
38489         if(this.isHorizontal){
38490             this._horizontalLayoutItems( items , isInstant );
38491             return;
38492         }
38493         
38494 //        if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
38495 //            this._verticalAlternativeLayoutItems( items , isInstant );
38496 //            return;
38497 //        }
38498         
38499         this._verticalLayoutItems( items , isInstant );
38500         
38501     },
38502     
38503     _verticalLayoutItems : function ( items , isInstant)
38504     {
38505         if ( !items || !items.length ) {
38506             return;
38507         }
38508         
38509         var standard = [
38510             ['xs', 'xs', 'xs', 'tall'],
38511             ['xs', 'xs', 'tall'],
38512             ['xs', 'xs', 'sm'],
38513             ['xs', 'xs', 'xs'],
38514             ['xs', 'tall'],
38515             ['xs', 'sm'],
38516             ['xs', 'xs'],
38517             ['xs'],
38518             
38519             ['sm', 'xs', 'xs'],
38520             ['sm', 'xs'],
38521             ['sm'],
38522             
38523             ['tall', 'xs', 'xs', 'xs'],
38524             ['tall', 'xs', 'xs'],
38525             ['tall', 'xs'],
38526             ['tall']
38527             
38528         ];
38529         
38530         var queue = [];
38531         
38532         var boxes = [];
38533         
38534         var box = [];
38535         
38536         Roo.each(items, function(item, k){
38537             
38538             switch (item.size) {
38539                 // these layouts take up a full box,
38540                 case 'md' :
38541                 case 'md-left' :
38542                 case 'md-right' :
38543                 case 'wide' :
38544                     
38545                     if(box.length){
38546                         boxes.push(box);
38547                         box = [];
38548                     }
38549                     
38550                     boxes.push([item]);
38551                     
38552                     break;
38553                     
38554                 case 'xs' :
38555                 case 'sm' :
38556                 case 'tall' :
38557                     
38558                     box.push(item);
38559                     
38560                     break;
38561                 default :
38562                     break;
38563                     
38564             }
38565             
38566         }, this);
38567         
38568         if(box.length){
38569             boxes.push(box);
38570             box = [];
38571         }
38572         
38573         var filterPattern = function(box, length)
38574         {
38575             if(!box.length){
38576                 return;
38577             }
38578             
38579             var match = false;
38580             
38581             var pattern = box.slice(0, length);
38582             
38583             var format = [];
38584             
38585             Roo.each(pattern, function(i){
38586                 format.push(i.size);
38587             }, this);
38588             
38589             Roo.each(standard, function(s){
38590                 
38591                 if(String(s) != String(format)){
38592                     return;
38593                 }
38594                 
38595                 match = true;
38596                 return false;
38597                 
38598             }, this);
38599             
38600             if(!match && length == 1){
38601                 return;
38602             }
38603             
38604             if(!match){
38605                 filterPattern(box, length - 1);
38606                 return;
38607             }
38608                 
38609             queue.push(pattern);
38610
38611             box = box.slice(length, box.length);
38612
38613             filterPattern(box, 4);
38614
38615             return;
38616             
38617         }
38618         
38619         Roo.each(boxes, function(box, k){
38620             
38621             if(!box.length){
38622                 return;
38623             }
38624             
38625             if(box.length == 1){
38626                 queue.push(box);
38627                 return;
38628             }
38629             
38630             filterPattern(box, 4);
38631             
38632         }, this);
38633         
38634         this._processVerticalLayoutQueue( queue, isInstant );
38635         
38636     },
38637     
38638 //    _verticalAlternativeLayoutItems : function( items , isInstant )
38639 //    {
38640 //        if ( !items || !items.length ) {
38641 //            return;
38642 //        }
38643 //
38644 //        this._processVerticalAlternativeLayoutQueue( items, isInstant );
38645 //        
38646 //    },
38647     
38648     _horizontalLayoutItems : function ( items , isInstant)
38649     {
38650         if ( !items || !items.length || items.length < 3) {
38651             return;
38652         }
38653         
38654         items.reverse();
38655         
38656         var eItems = items.slice(0, 3);
38657         
38658         items = items.slice(3, items.length);
38659         
38660         var standard = [
38661             ['xs', 'xs', 'xs', 'wide'],
38662             ['xs', 'xs', 'wide'],
38663             ['xs', 'xs', 'sm'],
38664             ['xs', 'xs', 'xs'],
38665             ['xs', 'wide'],
38666             ['xs', 'sm'],
38667             ['xs', 'xs'],
38668             ['xs'],
38669             
38670             ['sm', 'xs', 'xs'],
38671             ['sm', 'xs'],
38672             ['sm'],
38673             
38674             ['wide', 'xs', 'xs', 'xs'],
38675             ['wide', 'xs', 'xs'],
38676             ['wide', 'xs'],
38677             ['wide'],
38678             
38679             ['wide-thin']
38680         ];
38681         
38682         var queue = [];
38683         
38684         var boxes = [];
38685         
38686         var box = [];
38687         
38688         Roo.each(items, function(item, k){
38689             
38690             switch (item.size) {
38691                 case 'md' :
38692                 case 'md-left' :
38693                 case 'md-right' :
38694                 case 'tall' :
38695                     
38696                     if(box.length){
38697                         boxes.push(box);
38698                         box = [];
38699                     }
38700                     
38701                     boxes.push([item]);
38702                     
38703                     break;
38704                     
38705                 case 'xs' :
38706                 case 'sm' :
38707                 case 'wide' :
38708                 case 'wide-thin' :
38709                     
38710                     box.push(item);
38711                     
38712                     break;
38713                 default :
38714                     break;
38715                     
38716             }
38717             
38718         }, this);
38719         
38720         if(box.length){
38721             boxes.push(box);
38722             box = [];
38723         }
38724         
38725         var filterPattern = function(box, length)
38726         {
38727             if(!box.length){
38728                 return;
38729             }
38730             
38731             var match = false;
38732             
38733             var pattern = box.slice(0, length);
38734             
38735             var format = [];
38736             
38737             Roo.each(pattern, function(i){
38738                 format.push(i.size);
38739             }, this);
38740             
38741             Roo.each(standard, function(s){
38742                 
38743                 if(String(s) != String(format)){
38744                     return;
38745                 }
38746                 
38747                 match = true;
38748                 return false;
38749                 
38750             }, this);
38751             
38752             if(!match && length == 1){
38753                 return;
38754             }
38755             
38756             if(!match){
38757                 filterPattern(box, length - 1);
38758                 return;
38759             }
38760                 
38761             queue.push(pattern);
38762
38763             box = box.slice(length, box.length);
38764
38765             filterPattern(box, 4);
38766
38767             return;
38768             
38769         }
38770         
38771         Roo.each(boxes, function(box, k){
38772             
38773             if(!box.length){
38774                 return;
38775             }
38776             
38777             if(box.length == 1){
38778                 queue.push(box);
38779                 return;
38780             }
38781             
38782             filterPattern(box, 4);
38783             
38784         }, this);
38785         
38786         
38787         var prune = [];
38788         
38789         var pos = this.el.getBox(true);
38790         
38791         var minX = pos.x;
38792         
38793         var maxX = pos.right - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
38794         
38795         var hit_end = false;
38796         
38797         Roo.each(queue, function(box){
38798             
38799             if(hit_end){
38800                 
38801                 Roo.each(box, function(b){
38802                 
38803                     b.el.setVisibilityMode(Roo.Element.DISPLAY);
38804                     b.el.hide();
38805
38806                 }, this);
38807
38808                 return;
38809             }
38810             
38811             var mx = 0;
38812             
38813             Roo.each(box, function(b){
38814                 
38815                 b.el.setVisibilityMode(Roo.Element.DISPLAY);
38816                 b.el.show();
38817
38818                 mx = Math.max(mx, b.x);
38819                 
38820             }, this);
38821             
38822             maxX = maxX - this.unitWidth * mx - this.gutter * (mx - 1) - this.padWidth;
38823             
38824             if(maxX < minX){
38825                 
38826                 Roo.each(box, function(b){
38827                 
38828                     b.el.setVisibilityMode(Roo.Element.DISPLAY);
38829                     b.el.hide();
38830                     
38831                 }, this);
38832                 
38833                 hit_end = true;
38834                 
38835                 return;
38836             }
38837             
38838             prune.push(box);
38839             
38840         }, this);
38841         
38842         this._processHorizontalLayoutQueue( prune, eItems, isInstant );
38843     },
38844     
38845     /** Sets position of item in DOM
38846     * @param {Element} item
38847     * @param {Number} x - horizontal position
38848     * @param {Number} y - vertical position
38849     * @param {Boolean} isInstant - disables transitions
38850     */
38851     _processVerticalLayoutQueue : function( queue, isInstant )
38852     {
38853         var pos = this.el.getBox(true);
38854         var x = pos.x;
38855         var y = pos.y;
38856         var maxY = [];
38857         
38858         for (var i = 0; i < this.cols; i++){
38859             maxY[i] = pos.y;
38860         }
38861         
38862         Roo.each(queue, function(box, k){
38863             
38864             var col = k % this.cols;
38865             
38866             Roo.each(box, function(b,kk){
38867                 
38868                 b.el.position('absolute');
38869                 
38870                 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
38871                 var height = Math.floor(this.unitHeight * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
38872                 
38873                 if(b.size == 'md-left' || b.size == 'md-right'){
38874                     width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
38875                     height = Math.floor(this.unitHeight * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
38876                 }
38877                 
38878                 b.el.setWidth(width);
38879                 b.el.setHeight(height);
38880                 // iframe?
38881                 b.el.select('iframe',true).setSize(width,height);
38882                 
38883             }, this);
38884             
38885             for (var i = 0; i < this.cols; i++){
38886                 
38887                 if(maxY[i] < maxY[col]){
38888                     col = i;
38889                     continue;
38890                 }
38891                 
38892                 col = Math.min(col, i);
38893                 
38894             }
38895             
38896             x = pos.x + col * (this.colWidth + this.padWidth);
38897             
38898             y = maxY[col];
38899             
38900             var positions = [];
38901             
38902             switch (box.length){
38903                 case 1 :
38904                     positions = this.getVerticalOneBoxColPositions(x, y, box);
38905                     break;
38906                 case 2 :
38907                     positions = this.getVerticalTwoBoxColPositions(x, y, box);
38908                     break;
38909                 case 3 :
38910                     positions = this.getVerticalThreeBoxColPositions(x, y, box);
38911                     break;
38912                 case 4 :
38913                     positions = this.getVerticalFourBoxColPositions(x, y, box);
38914                     break;
38915                 default :
38916                     break;
38917             }
38918             
38919             Roo.each(box, function(b,kk){
38920                 
38921                 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
38922                 
38923                 var sz = b.el.getSize();
38924                 
38925                 maxY[col] = Math.max(maxY[col], positions[kk].y + sz.height + this.padWidth);
38926                 
38927             }, this);
38928             
38929         }, this);
38930         
38931         var mY = 0;
38932         
38933         for (var i = 0; i < this.cols; i++){
38934             mY = Math.max(mY, maxY[i]);
38935         }
38936         
38937         this.el.setHeight(mY - pos.y);
38938         
38939     },
38940     
38941 //    _processVerticalAlternativeLayoutQueue : function( items, isInstant )
38942 //    {
38943 //        var pos = this.el.getBox(true);
38944 //        var x = pos.x;
38945 //        var y = pos.y;
38946 //        var maxX = pos.right;
38947 //        
38948 //        var maxHeight = 0;
38949 //        
38950 //        Roo.each(items, function(item, k){
38951 //            
38952 //            var c = k % 2;
38953 //            
38954 //            item.el.position('absolute');
38955 //                
38956 //            var width = Math.floor(this.colWidth + item.el.getPadding('lr'));
38957 //
38958 //            item.el.setWidth(width);
38959 //
38960 //            var height = Math.floor(this.colWidth * item.y / item.x + item.el.getPadding('tb'));
38961 //
38962 //            item.el.setHeight(height);
38963 //            
38964 //            if(c == 0){
38965 //                item.el.setXY([x, y], isInstant ? false : true);
38966 //            } else {
38967 //                item.el.setXY([maxX - width, y], isInstant ? false : true);
38968 //            }
38969 //            
38970 //            y = y + height + this.alternativePadWidth;
38971 //            
38972 //            maxHeight = maxHeight + height + this.alternativePadWidth;
38973 //            
38974 //        }, this);
38975 //        
38976 //        this.el.setHeight(maxHeight);
38977 //        
38978 //    },
38979     
38980     _processHorizontalLayoutQueue : function( queue, eItems, isInstant )
38981     {
38982         var pos = this.el.getBox(true);
38983         
38984         var minX = pos.x;
38985         var minY = pos.y;
38986         
38987         var maxX = pos.right;
38988         
38989         this._processHorizontalEndItem(eItems, maxX, minX, minY, isInstant);
38990         
38991         var maxX = maxX - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
38992         
38993         Roo.each(queue, function(box, k){
38994             
38995             Roo.each(box, function(b, kk){
38996                 
38997                 b.el.position('absolute');
38998                 
38999                 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
39000                 var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
39001                 
39002                 if(b.size == 'md-left' || b.size == 'md-right'){
39003                     width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
39004                     height = Math.floor(this.unitWidth * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
39005                 }
39006                 
39007                 b.el.setWidth(width);
39008                 b.el.setHeight(height);
39009                 
39010             }, this);
39011             
39012             if(!box.length){
39013                 return;
39014             }
39015             
39016             var positions = [];
39017             
39018             switch (box.length){
39019                 case 1 :
39020                     positions = this.getHorizontalOneBoxColPositions(maxX, minY, box);
39021                     break;
39022                 case 2 :
39023                     positions = this.getHorizontalTwoBoxColPositions(maxX, minY, box);
39024                     break;
39025                 case 3 :
39026                     positions = this.getHorizontalThreeBoxColPositions(maxX, minY, box);
39027                     break;
39028                 case 4 :
39029                     positions = this.getHorizontalFourBoxColPositions(maxX, minY, box);
39030                     break;
39031                 default :
39032                     break;
39033             }
39034             
39035             Roo.each(box, function(b,kk){
39036                 
39037                 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
39038                 
39039                 maxX = Math.min(maxX, positions[kk].x - this.padWidth);
39040                 
39041             }, this);
39042             
39043         }, this);
39044         
39045     },
39046     
39047     _processHorizontalEndItem : function(eItems, maxX, minX, minY, isInstant)
39048     {
39049         Roo.each(eItems, function(b,k){
39050             
39051             b.size = (k == 0) ? 'sm' : 'xs';
39052             b.x = (k == 0) ? 2 : 1;
39053             b.y = (k == 0) ? 2 : 1;
39054             
39055             b.el.position('absolute');
39056             
39057             var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
39058                 
39059             b.el.setWidth(width);
39060             
39061             var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
39062             
39063             b.el.setHeight(height);
39064             
39065         }, this);
39066
39067         var positions = [];
39068         
39069         positions.push({
39070             x : maxX - this.unitWidth * 2 - this.gutter,
39071             y : minY
39072         });
39073         
39074         positions.push({
39075             x : maxX - this.unitWidth,
39076             y : minY + (this.unitWidth + this.gutter) * 2
39077         });
39078         
39079         positions.push({
39080             x : maxX - this.unitWidth * 3 - this.gutter * 2,
39081             y : minY
39082         });
39083         
39084         Roo.each(eItems, function(b,k){
39085             
39086             b.el.setXY([positions[k].x, positions[k].y], isInstant ? false : true);
39087
39088         }, this);
39089         
39090     },
39091     
39092     getVerticalOneBoxColPositions : function(x, y, box)
39093     {
39094         var pos = [];
39095         
39096         var rand = Math.floor(Math.random() * ((4 - box[0].x)));
39097         
39098         if(box[0].size == 'md-left'){
39099             rand = 0;
39100         }
39101         
39102         if(box[0].size == 'md-right'){
39103             rand = 1;
39104         }
39105         
39106         pos.push({
39107             x : x + (this.unitWidth + this.gutter) * rand,
39108             y : y
39109         });
39110         
39111         return pos;
39112     },
39113     
39114     getVerticalTwoBoxColPositions : function(x, y, box)
39115     {
39116         var pos = [];
39117         
39118         if(box[0].size == 'xs'){
39119             
39120             pos.push({
39121                 x : x,
39122                 y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[1].y))
39123             });
39124
39125             pos.push({
39126                 x : x + (this.unitWidth + this.gutter) * (3 - box[1].x),
39127                 y : y
39128             });
39129             
39130             return pos;
39131             
39132         }
39133         
39134         pos.push({
39135             x : x,
39136             y : y
39137         });
39138
39139         pos.push({
39140             x : x + (this.unitWidth + this.gutter) * 2,
39141             y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[0].y))
39142         });
39143         
39144         return pos;
39145         
39146     },
39147     
39148     getVerticalThreeBoxColPositions : function(x, y, box)
39149     {
39150         var pos = [];
39151         
39152         if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
39153             
39154             pos.push({
39155                 x : x,
39156                 y : y
39157             });
39158
39159             pos.push({
39160                 x : x + (this.unitWidth + this.gutter) * 1,
39161                 y : y
39162             });
39163             
39164             pos.push({
39165                 x : x + (this.unitWidth + this.gutter) * 2,
39166                 y : y
39167             });
39168             
39169             return pos;
39170             
39171         }
39172         
39173         if(box[0].size == 'xs' && box[1].size == 'xs'){
39174             
39175             pos.push({
39176                 x : x,
39177                 y : y
39178             });
39179
39180             pos.push({
39181                 x : x,
39182                 y : y + ((this.unitHeight + this.gutter) * (box[2].y - 1))
39183             });
39184             
39185             pos.push({
39186                 x : x + (this.unitWidth + this.gutter) * 1,
39187                 y : y
39188             });
39189             
39190             return pos;
39191             
39192         }
39193         
39194         pos.push({
39195             x : x,
39196             y : y
39197         });
39198
39199         pos.push({
39200             x : x + (this.unitWidth + this.gutter) * 2,
39201             y : y
39202         });
39203
39204         pos.push({
39205             x : x + (this.unitWidth + this.gutter) * 2,
39206             y : y + (this.unitHeight + this.gutter) * (box[0].y - 1)
39207         });
39208             
39209         return pos;
39210         
39211     },
39212     
39213     getVerticalFourBoxColPositions : function(x, y, box)
39214     {
39215         var pos = [];
39216         
39217         if(box[0].size == 'xs'){
39218             
39219             pos.push({
39220                 x : x,
39221                 y : y
39222             });
39223
39224             pos.push({
39225                 x : x,
39226                 y : y + (this.unitHeight + this.gutter) * 1
39227             });
39228             
39229             pos.push({
39230                 x : x,
39231                 y : y + (this.unitHeight + this.gutter) * 2
39232             });
39233             
39234             pos.push({
39235                 x : x + (this.unitWidth + this.gutter) * 1,
39236                 y : y
39237             });
39238             
39239             return pos;
39240             
39241         }
39242         
39243         pos.push({
39244             x : x,
39245             y : y
39246         });
39247
39248         pos.push({
39249             x : x + (this.unitWidth + this.gutter) * 2,
39250             y : y
39251         });
39252
39253         pos.push({
39254             x : x + (this.unitHeightunitWidth + this.gutter) * 2,
39255             y : y + (this.unitHeight + this.gutter) * 1
39256         });
39257
39258         pos.push({
39259             x : x + (this.unitWidth + this.gutter) * 2,
39260             y : y + (this.unitWidth + this.gutter) * 2
39261         });
39262
39263         return pos;
39264         
39265     },
39266     
39267     getHorizontalOneBoxColPositions : function(maxX, minY, box)
39268     {
39269         var pos = [];
39270         
39271         if(box[0].size == 'md-left'){
39272             pos.push({
39273                 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
39274                 y : minY
39275             });
39276             
39277             return pos;
39278         }
39279         
39280         if(box[0].size == 'md-right'){
39281             pos.push({
39282                 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
39283                 y : minY + (this.unitWidth + this.gutter) * 1
39284             });
39285             
39286             return pos;
39287         }
39288         
39289         var rand = Math.floor(Math.random() * (4 - box[0].y));
39290         
39291         pos.push({
39292             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
39293             y : minY + (this.unitWidth + this.gutter) * rand
39294         });
39295         
39296         return pos;
39297         
39298     },
39299     
39300     getHorizontalTwoBoxColPositions : function(maxX, minY, box)
39301     {
39302         var pos = [];
39303         
39304         if(box[0].size == 'xs'){
39305             
39306             pos.push({
39307                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
39308                 y : minY
39309             });
39310
39311             pos.push({
39312                 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
39313                 y : minY + (this.unitWidth + this.gutter) * (3 - box[1].y)
39314             });
39315             
39316             return pos;
39317             
39318         }
39319         
39320         pos.push({
39321             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
39322             y : minY
39323         });
39324
39325         pos.push({
39326             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
39327             y : minY + (this.unitWidth + this.gutter) * 2
39328         });
39329         
39330         return pos;
39331         
39332     },
39333     
39334     getHorizontalThreeBoxColPositions : function(maxX, minY, box)
39335     {
39336         var pos = [];
39337         
39338         if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
39339             
39340             pos.push({
39341                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
39342                 y : minY
39343             });
39344
39345             pos.push({
39346                 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
39347                 y : minY + (this.unitWidth + this.gutter) * 1
39348             });
39349             
39350             pos.push({
39351                 x : maxX - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
39352                 y : minY + (this.unitWidth + this.gutter) * 2
39353             });
39354             
39355             return pos;
39356             
39357         }
39358         
39359         if(box[0].size == 'xs' && box[1].size == 'xs'){
39360             
39361             pos.push({
39362                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
39363                 y : minY
39364             });
39365
39366             pos.push({
39367                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
39368                 y : minY
39369             });
39370             
39371             pos.push({
39372                 x : maxX - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
39373                 y : minY + (this.unitWidth + this.gutter) * 1
39374             });
39375             
39376             return pos;
39377             
39378         }
39379         
39380         pos.push({
39381             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
39382             y : minY
39383         });
39384
39385         pos.push({
39386             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
39387             y : minY + (this.unitWidth + this.gutter) * 2
39388         });
39389
39390         pos.push({
39391             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
39392             y : minY + (this.unitWidth + this.gutter) * 2
39393         });
39394             
39395         return pos;
39396         
39397     },
39398     
39399     getHorizontalFourBoxColPositions : function(maxX, minY, box)
39400     {
39401         var pos = [];
39402         
39403         if(box[0].size == 'xs'){
39404             
39405             pos.push({
39406                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
39407                 y : minY
39408             });
39409
39410             pos.push({
39411                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
39412                 y : minY
39413             });
39414             
39415             pos.push({
39416                 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),
39417                 y : minY
39418             });
39419             
39420             pos.push({
39421                 x : maxX - this.unitWidth * box[3].x - this.gutter * (box[3].x - 1),
39422                 y : minY + (this.unitWidth + this.gutter) * 1
39423             });
39424             
39425             return pos;
39426             
39427         }
39428         
39429         pos.push({
39430             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
39431             y : minY
39432         });
39433         
39434         pos.push({
39435             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
39436             y : minY + (this.unitWidth + this.gutter) * 2
39437         });
39438         
39439         pos.push({
39440             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
39441             y : minY + (this.unitWidth + this.gutter) * 2
39442         });
39443         
39444         pos.push({
39445             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),
39446             y : minY + (this.unitWidth + this.gutter) * 2
39447         });
39448
39449         return pos;
39450         
39451     },
39452     
39453     /**
39454     * remove a Masonry Brick
39455     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to remove
39456     */
39457     removeBrick : function(brick_id)
39458     {
39459         if (!brick_id) {
39460             return;
39461         }
39462         
39463         for (var i = 0; i<this.bricks.length; i++) {
39464             if (this.bricks[i].id == brick_id) {
39465                 this.bricks.splice(i,1);
39466                 this.el.dom.removeChild(Roo.get(brick_id).dom);
39467                 this.initial();
39468             }
39469         }
39470     },
39471     
39472     /**
39473     * adds a Masonry Brick
39474     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
39475     */
39476     addBrick : function(cfg)
39477     {
39478         var cn = new Roo.bootstrap.MasonryBrick(cfg);
39479         //this.register(cn);
39480         cn.parentId = this.id;
39481         cn.render(this.el);
39482         return cn;
39483     },
39484     
39485     /**
39486     * register a Masonry Brick
39487     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
39488     */
39489     
39490     register : function(brick)
39491     {
39492         this.bricks.push(brick);
39493         brick.masonryId = this.id;
39494     },
39495     
39496     /**
39497     * clear all the Masonry Brick
39498     */
39499     clearAll : function()
39500     {
39501         this.bricks = [];
39502         //this.getChildContainer().dom.innerHTML = "";
39503         this.el.dom.innerHTML = '';
39504     },
39505     
39506     getSelected : function()
39507     {
39508         if (!this.selectedBrick) {
39509             return false;
39510         }
39511         
39512         return this.selectedBrick;
39513     }
39514 });
39515
39516 Roo.apply(Roo.bootstrap.LayoutMasonry, {
39517     
39518     groups: {},
39519      /**
39520     * register a Masonry Layout
39521     * @param {Roo.bootstrap.LayoutMasonry} the masonry layout to add
39522     */
39523     
39524     register : function(layout)
39525     {
39526         this.groups[layout.id] = layout;
39527     },
39528     /**
39529     * fetch a  Masonry Layout based on the masonry layout ID
39530     * @param {string} the masonry layout to add
39531     * @returns {Roo.bootstrap.LayoutMasonry} the masonry layout
39532     */
39533     
39534     get: function(layout_id) {
39535         if (typeof(this.groups[layout_id]) == 'undefined') {
39536             return false;
39537         }
39538         return this.groups[layout_id] ;
39539     }
39540     
39541     
39542     
39543 });
39544
39545  
39546
39547  /**
39548  *
39549  * This is based on 
39550  * http://masonry.desandro.com
39551  *
39552  * The idea is to render all the bricks based on vertical width...
39553  *
39554  * The original code extends 'outlayer' - we might need to use that....
39555  * 
39556  */
39557
39558
39559 /**
39560  * @class Roo.bootstrap.LayoutMasonryAuto
39561  * @extends Roo.bootstrap.Component
39562  * Bootstrap Layout Masonry class
39563  * 
39564  * @constructor
39565  * Create a new Element
39566  * @param {Object} config The config object
39567  */
39568
39569 Roo.bootstrap.LayoutMasonryAuto = function(config){
39570     Roo.bootstrap.LayoutMasonryAuto.superclass.constructor.call(this, config);
39571 };
39572
39573 Roo.extend(Roo.bootstrap.LayoutMasonryAuto, Roo.bootstrap.Component,  {
39574     
39575       /**
39576      * @cfg {Boolean} isFitWidth  - resize the width..
39577      */   
39578     isFitWidth : false,  // options..
39579     /**
39580      * @cfg {Boolean} isOriginLeft = left align?
39581      */   
39582     isOriginLeft : true,
39583     /**
39584      * @cfg {Boolean} isOriginTop = top align?
39585      */   
39586     isOriginTop : false,
39587     /**
39588      * @cfg {Boolean} isLayoutInstant = no animation?
39589      */   
39590     isLayoutInstant : false, // needed?
39591     /**
39592      * @cfg {Boolean} isResizingContainer = not sure if this is used..
39593      */   
39594     isResizingContainer : true,
39595     /**
39596      * @cfg {Number} columnWidth  width of the columns 
39597      */   
39598     
39599     columnWidth : 0,
39600     
39601     /**
39602      * @cfg {Number} maxCols maximum number of columns
39603      */   
39604     
39605     maxCols: 0,
39606     /**
39607      * @cfg {Number} padHeight padding below box..
39608      */   
39609     
39610     padHeight : 10, 
39611     
39612     /**
39613      * @cfg {Boolean} isAutoInitial defalut true
39614      */   
39615     
39616     isAutoInitial : true, 
39617     
39618     // private?
39619     gutter : 0,
39620     
39621     containerWidth: 0,
39622     initialColumnWidth : 0,
39623     currentSize : null,
39624     
39625     colYs : null, // array.
39626     maxY : 0,
39627     padWidth: 10,
39628     
39629     
39630     tag: 'div',
39631     cls: '',
39632     bricks: null, //CompositeElement
39633     cols : 0, // array?
39634     // element : null, // wrapped now this.el
39635     _isLayoutInited : null, 
39636     
39637     
39638     getAutoCreate : function(){
39639         
39640         var cfg = {
39641             tag: this.tag,
39642             cls: 'blog-masonary-wrapper ' + this.cls,
39643             cn : {
39644                 cls : 'mas-boxes masonary'
39645             }
39646         };
39647         
39648         return cfg;
39649     },
39650     
39651     getChildContainer: function( )
39652     {
39653         if (this.boxesEl) {
39654             return this.boxesEl;
39655         }
39656         
39657         this.boxesEl = this.el.select('.mas-boxes').first();
39658         
39659         return this.boxesEl;
39660     },
39661     
39662     
39663     initEvents : function()
39664     {
39665         var _this = this;
39666         
39667         if(this.isAutoInitial){
39668             Roo.log('hook children rendered');
39669             this.on('childrenrendered', function() {
39670                 Roo.log('children rendered');
39671                 _this.initial();
39672             } ,this);
39673         }
39674         
39675     },
39676     
39677     initial : function()
39678     {
39679         this.reloadItems();
39680
39681         this.currentSize = this.el.getBox(true);
39682
39683         /// was window resize... - let's see if this works..
39684         Roo.EventManager.onWindowResize(this.resize, this); 
39685
39686         if(!this.isAutoInitial){
39687             this.layout();
39688             return;
39689         }
39690         
39691         this.layout.defer(500,this);
39692     },
39693     
39694     reloadItems: function()
39695     {
39696         this.bricks = this.el.select('.masonry-brick', true);
39697         
39698         this.bricks.each(function(b) {
39699             //Roo.log(b.getSize());
39700             if (!b.attr('originalwidth')) {
39701                 b.attr('originalwidth',  b.getSize().width);
39702             }
39703             
39704         });
39705         
39706         Roo.log(this.bricks.elements.length);
39707     },
39708     
39709     resize : function()
39710     {
39711         Roo.log('resize');
39712         var cs = this.el.getBox(true);
39713         
39714         if (this.currentSize.width == cs.width && this.currentSize.x == cs.x ) {
39715             Roo.log("no change in with or X");
39716             return;
39717         }
39718         this.currentSize = cs;
39719         this.layout();
39720     },
39721     
39722     layout : function()
39723     {
39724          Roo.log('layout');
39725         this._resetLayout();
39726         //this._manageStamps();
39727       
39728         // don't animate first layout
39729         var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
39730         this.layoutItems( isInstant );
39731       
39732         // flag for initalized
39733         this._isLayoutInited = true;
39734     },
39735     
39736     layoutItems : function( isInstant )
39737     {
39738         //var items = this._getItemsForLayout( this.items );
39739         // original code supports filtering layout items.. we just ignore it..
39740         
39741         this._layoutItems( this.bricks , isInstant );
39742       
39743         this._postLayout();
39744     },
39745     _layoutItems : function ( items , isInstant)
39746     {
39747        //this.fireEvent( 'layout', this, items );
39748     
39749
39750         if ( !items || !items.elements.length ) {
39751           // no items, emit event with empty array
39752             return;
39753         }
39754
39755         var queue = [];
39756         items.each(function(item) {
39757             Roo.log("layout item");
39758             Roo.log(item);
39759             // get x/y object from method
39760             var position = this._getItemLayoutPosition( item );
39761             // enqueue
39762             position.item = item;
39763             position.isInstant = isInstant; // || item.isLayoutInstant; << not set yet...
39764             queue.push( position );
39765         }, this);
39766       
39767         this._processLayoutQueue( queue );
39768     },
39769     /** Sets position of item in DOM
39770     * @param {Element} item
39771     * @param {Number} x - horizontal position
39772     * @param {Number} y - vertical position
39773     * @param {Boolean} isInstant - disables transitions
39774     */
39775     _processLayoutQueue : function( queue )
39776     {
39777         for ( var i=0, len = queue.length; i < len; i++ ) {
39778             var obj = queue[i];
39779             obj.item.position('absolute');
39780             obj.item.setXY([obj.x,obj.y], obj.isInstant ? false : true);
39781         }
39782     },
39783       
39784     
39785     /**
39786     * Any logic you want to do after each layout,
39787     * i.e. size the container
39788     */
39789     _postLayout : function()
39790     {
39791         this.resizeContainer();
39792     },
39793     
39794     resizeContainer : function()
39795     {
39796         if ( !this.isResizingContainer ) {
39797             return;
39798         }
39799         var size = this._getContainerSize();
39800         if ( size ) {
39801             this.el.setSize(size.width,size.height);
39802             this.boxesEl.setSize(size.width,size.height);
39803         }
39804     },
39805     
39806     
39807     
39808     _resetLayout : function()
39809     {
39810         //this.getSize();  // -- does not really do anything.. it probably applies left/right etc. to obuject but not used
39811         this.colWidth = this.el.getWidth();
39812         //this.gutter = this.el.getWidth(); 
39813         
39814         this.measureColumns();
39815
39816         // reset column Y
39817         var i = this.cols;
39818         this.colYs = [];
39819         while (i--) {
39820             this.colYs.push( 0 );
39821         }
39822     
39823         this.maxY = 0;
39824     },
39825
39826     measureColumns : function()
39827     {
39828         this.getContainerWidth();
39829       // if columnWidth is 0, default to outerWidth of first item
39830         if ( !this.columnWidth ) {
39831             var firstItem = this.bricks.first();
39832             Roo.log(firstItem);
39833             this.columnWidth  = this.containerWidth;
39834             if (firstItem && firstItem.attr('originalwidth') ) {
39835                 this.columnWidth = 1* (firstItem.attr('originalwidth') || firstItem.getWidth());
39836             }
39837             // columnWidth fall back to item of first element
39838             Roo.log("set column width?");
39839                         this.initialColumnWidth = this.columnWidth  ;
39840
39841             // if first elem has no width, default to size of container
39842             
39843         }
39844         
39845         
39846         if (this.initialColumnWidth) {
39847             this.columnWidth = this.initialColumnWidth;
39848         }
39849         
39850         
39851             
39852         // column width is fixed at the top - however if container width get's smaller we should
39853         // reduce it...
39854         
39855         // this bit calcs how man columns..
39856             
39857         var columnWidth = this.columnWidth += this.gutter;
39858       
39859         // calculate columns
39860         var containerWidth = this.containerWidth + this.gutter;
39861         
39862         var cols = (containerWidth - this.padWidth) / (columnWidth - this.padWidth);
39863         // fix rounding errors, typically with gutters
39864         var excess = columnWidth - containerWidth % columnWidth;
39865         
39866         
39867         // if overshoot is less than a pixel, round up, otherwise floor it
39868         var mathMethod = excess && excess < 1 ? 'round' : 'floor';
39869         cols = Math[ mathMethod ]( cols );
39870         this.cols = Math.max( cols, 1 );
39871         this.cols = this.maxCols > 0 ? Math.min( this.cols, this.maxCols ) : this.cols;
39872         
39873          // padding positioning..
39874         var totalColWidth = this.cols * this.columnWidth;
39875         var padavail = this.containerWidth - totalColWidth;
39876         // so for 2 columns - we need 3 'pads'
39877         
39878         var padNeeded = (1+this.cols) * this.padWidth;
39879         
39880         var padExtra = Math.floor((padavail - padNeeded) / this.cols);
39881         
39882         this.columnWidth += padExtra
39883         //this.padWidth = Math.floor(padavail /  ( this.cols));
39884         
39885         // adjust colum width so that padding is fixed??
39886         
39887         // we have 3 columns ... total = width * 3
39888         // we have X left over... that should be used by 
39889         
39890         //if (this.expandC) {
39891             
39892         //}
39893         
39894         
39895         
39896     },
39897     
39898     getContainerWidth : function()
39899     {
39900        /* // container is parent if fit width
39901         var container = this.isFitWidth ? this.element.parentNode : this.element;
39902         // check that this.size and size are there
39903         // IE8 triggers resize on body size change, so they might not be
39904         
39905         var size = getSize( container );  //FIXME
39906         this.containerWidth = size && size.innerWidth; //FIXME
39907         */
39908          
39909         this.containerWidth = this.el.getBox(true).width;  //maybe use getComputedWidth
39910         
39911     },
39912     
39913     _getItemLayoutPosition : function( item )  // what is item?
39914     {
39915         // we resize the item to our columnWidth..
39916       
39917         item.setWidth(this.columnWidth);
39918         item.autoBoxAdjust  = false;
39919         
39920         var sz = item.getSize();
39921  
39922         // how many columns does this brick span
39923         var remainder = this.containerWidth % this.columnWidth;
39924         
39925         var mathMethod = remainder && remainder < 1 ? 'round' : 'ceil';
39926         // round if off by 1 pixel, otherwise use ceil
39927         var colSpan = Math[ mathMethod ]( sz.width  / this.columnWidth );
39928         colSpan = Math.min( colSpan, this.cols );
39929         
39930         // normally this should be '1' as we dont' currently allow multi width columns..
39931         
39932         var colGroup = this._getColGroup( colSpan );
39933         // get the minimum Y value from the columns
39934         var minimumY = Math.min.apply( Math, colGroup );
39935         Roo.log([ 'setHeight',  minimumY, sz.height, setHeight ]);
39936         
39937         var shortColIndex = colGroup.indexOf(  minimumY ); // broken on ie8..?? probably...
39938          
39939         // position the brick
39940         var position = {
39941             x: this.currentSize.x + (this.padWidth /2) + ((this.columnWidth + this.padWidth )* shortColIndex),
39942             y: this.currentSize.y + minimumY + this.padHeight
39943         };
39944         
39945         Roo.log(position);
39946         // apply setHeight to necessary columns
39947         var setHeight = minimumY + sz.height + this.padHeight;
39948         //Roo.log([ 'setHeight',  minimumY, sz.height, setHeight ]);
39949         
39950         var setSpan = this.cols + 1 - colGroup.length;
39951         for ( var i = 0; i < setSpan; i++ ) {
39952           this.colYs[ shortColIndex + i ] = setHeight ;
39953         }
39954       
39955         return position;
39956     },
39957     
39958     /**
39959      * @param {Number} colSpan - number of columns the element spans
39960      * @returns {Array} colGroup
39961      */
39962     _getColGroup : function( colSpan )
39963     {
39964         if ( colSpan < 2 ) {
39965           // if brick spans only one column, use all the column Ys
39966           return this.colYs;
39967         }
39968       
39969         var colGroup = [];
39970         // how many different places could this brick fit horizontally
39971         var groupCount = this.cols + 1 - colSpan;
39972         // for each group potential horizontal position
39973         for ( var i = 0; i < groupCount; i++ ) {
39974           // make an array of colY values for that one group
39975           var groupColYs = this.colYs.slice( i, i + colSpan );
39976           // and get the max value of the array
39977           colGroup[i] = Math.max.apply( Math, groupColYs );
39978         }
39979         return colGroup;
39980     },
39981     /*
39982     _manageStamp : function( stamp )
39983     {
39984         var stampSize =  stamp.getSize();
39985         var offset = stamp.getBox();
39986         // get the columns that this stamp affects
39987         var firstX = this.isOriginLeft ? offset.x : offset.right;
39988         var lastX = firstX + stampSize.width;
39989         var firstCol = Math.floor( firstX / this.columnWidth );
39990         firstCol = Math.max( 0, firstCol );
39991         
39992         var lastCol = Math.floor( lastX / this.columnWidth );
39993         // lastCol should not go over if multiple of columnWidth #425
39994         lastCol -= lastX % this.columnWidth ? 0 : 1;
39995         lastCol = Math.min( this.cols - 1, lastCol );
39996         
39997         // set colYs to bottom of the stamp
39998         var stampMaxY = ( this.isOriginTop ? offset.y : offset.bottom ) +
39999             stampSize.height;
40000             
40001         for ( var i = firstCol; i <= lastCol; i++ ) {
40002           this.colYs[i] = Math.max( stampMaxY, this.colYs[i] );
40003         }
40004     },
40005     */
40006     
40007     _getContainerSize : function()
40008     {
40009         this.maxY = Math.max.apply( Math, this.colYs );
40010         var size = {
40011             height: this.maxY
40012         };
40013       
40014         if ( this.isFitWidth ) {
40015             size.width = this._getContainerFitWidth();
40016         }
40017       
40018         return size;
40019     },
40020     
40021     _getContainerFitWidth : function()
40022     {
40023         var unusedCols = 0;
40024         // count unused columns
40025         var i = this.cols;
40026         while ( --i ) {
40027           if ( this.colYs[i] !== 0 ) {
40028             break;
40029           }
40030           unusedCols++;
40031         }
40032         // fit container to columns that have been used
40033         return ( this.cols - unusedCols ) * this.columnWidth - this.gutter;
40034     },
40035     
40036     needsResizeLayout : function()
40037     {
40038         var previousWidth = this.containerWidth;
40039         this.getContainerWidth();
40040         return previousWidth !== this.containerWidth;
40041     }
40042  
40043 });
40044
40045  
40046
40047  /*
40048  * - LGPL
40049  *
40050  * element
40051  * 
40052  */
40053
40054 /**
40055  * @class Roo.bootstrap.MasonryBrick
40056  * @extends Roo.bootstrap.Component
40057  * Bootstrap MasonryBrick class
40058  * 
40059  * @constructor
40060  * Create a new MasonryBrick
40061  * @param {Object} config The config object
40062  */
40063
40064 Roo.bootstrap.MasonryBrick = function(config){
40065     
40066     Roo.bootstrap.MasonryBrick.superclass.constructor.call(this, config);
40067     
40068     Roo.bootstrap.MasonryBrick.register(this);
40069     
40070     this.addEvents({
40071         // raw events
40072         /**
40073          * @event click
40074          * When a MasonryBrick is clcik
40075          * @param {Roo.bootstrap.MasonryBrick} this
40076          * @param {Roo.EventObject} e
40077          */
40078         "click" : true
40079     });
40080 };
40081
40082 Roo.extend(Roo.bootstrap.MasonryBrick, Roo.bootstrap.Component,  {
40083     
40084     /**
40085      * @cfg {String} title
40086      */   
40087     title : '',
40088     /**
40089      * @cfg {String} html
40090      */   
40091     html : '',
40092     /**
40093      * @cfg {String} bgimage
40094      */   
40095     bgimage : '',
40096     /**
40097      * @cfg {String} videourl
40098      */   
40099     videourl : '',
40100     /**
40101      * @cfg {String} cls
40102      */   
40103     cls : '',
40104     /**
40105      * @cfg {String} href
40106      */   
40107     href : '',
40108     /**
40109      * @cfg {String} size (xs|sm|md|md-left|md-right|tall|wide)
40110      */   
40111     size : 'xs',
40112     
40113     /**
40114      * @cfg {String} placetitle (center|bottom)
40115      */   
40116     placetitle : '',
40117     
40118     /**
40119      * @cfg {Boolean} isFitContainer defalut true
40120      */   
40121     isFitContainer : true, 
40122     
40123     /**
40124      * @cfg {Boolean} preventDefault defalut false
40125      */   
40126     preventDefault : false, 
40127     
40128     /**
40129      * @cfg {Boolean} inverse defalut false
40130      */   
40131     maskInverse : false, 
40132     
40133     getAutoCreate : function()
40134     {
40135         if(!this.isFitContainer){
40136             return this.getSplitAutoCreate();
40137         }
40138         
40139         var cls = 'masonry-brick masonry-brick-full';
40140         
40141         if(this.href.length){
40142             cls += ' masonry-brick-link';
40143         }
40144         
40145         if(this.bgimage.length){
40146             cls += ' masonry-brick-image';
40147         }
40148         
40149         if(this.maskInverse){
40150             cls += ' mask-inverse';
40151         }
40152         
40153         if(!this.html.length && !this.maskInverse && !this.videourl.length){
40154             cls += ' enable-mask';
40155         }
40156         
40157         if(this.size){
40158             cls += ' masonry-' + this.size + '-brick';
40159         }
40160         
40161         if(this.placetitle.length){
40162             
40163             switch (this.placetitle) {
40164                 case 'center' :
40165                     cls += ' masonry-center-title';
40166                     break;
40167                 case 'bottom' :
40168                     cls += ' masonry-bottom-title';
40169                     break;
40170                 default:
40171                     break;
40172             }
40173             
40174         } else {
40175             if(!this.html.length && !this.bgimage.length){
40176                 cls += ' masonry-center-title';
40177             }
40178
40179             if(!this.html.length && this.bgimage.length){
40180                 cls += ' masonry-bottom-title';
40181             }
40182         }
40183         
40184         if(this.cls){
40185             cls += ' ' + this.cls;
40186         }
40187         
40188         var cfg = {
40189             tag: (this.href.length) ? 'a' : 'div',
40190             cls: cls,
40191             cn: [
40192                 {
40193                     tag: 'div',
40194                     cls: 'masonry-brick-mask'
40195                 },
40196                 {
40197                     tag: 'div',
40198                     cls: 'masonry-brick-paragraph',
40199                     cn: []
40200                 }
40201             ]
40202         };
40203         
40204         if(this.href.length){
40205             cfg.href = this.href;
40206         }
40207         
40208         var cn = cfg.cn[1].cn;
40209         
40210         if(this.title.length){
40211             cn.push({
40212                 tag: 'h4',
40213                 cls: 'masonry-brick-title',
40214                 html: this.title
40215             });
40216         }
40217         
40218         if(this.html.length){
40219             cn.push({
40220                 tag: 'p',
40221                 cls: 'masonry-brick-text',
40222                 html: this.html
40223             });
40224         }
40225         
40226         if (!this.title.length && !this.html.length) {
40227             cfg.cn[1].cls += ' hide';
40228         }
40229         
40230         if(this.bgimage.length){
40231             cfg.cn.push({
40232                 tag: 'img',
40233                 cls: 'masonry-brick-image-view',
40234                 src: this.bgimage
40235             });
40236         }
40237         
40238         if(this.videourl.length){
40239             var vurl = this.videourl.replace(/https:\/\/youtu\.be/, 'https://www.youtube.com/embed/');
40240             // youtube support only?
40241             cfg.cn.push({
40242                 tag: 'iframe',
40243                 cls: 'masonry-brick-image-view',
40244                 src: vurl,
40245                 frameborder : 0,
40246                 allowfullscreen : true
40247             });
40248         }
40249         
40250         return cfg;
40251         
40252     },
40253     
40254     getSplitAutoCreate : function()
40255     {
40256         var cls = 'masonry-brick masonry-brick-split';
40257         
40258         if(this.href.length){
40259             cls += ' masonry-brick-link';
40260         }
40261         
40262         if(this.bgimage.length){
40263             cls += ' masonry-brick-image';
40264         }
40265         
40266         if(this.size){
40267             cls += ' masonry-' + this.size + '-brick';
40268         }
40269         
40270         switch (this.placetitle) {
40271             case 'center' :
40272                 cls += ' masonry-center-title';
40273                 break;
40274             case 'bottom' :
40275                 cls += ' masonry-bottom-title';
40276                 break;
40277             default:
40278                 if(!this.bgimage.length){
40279                     cls += ' masonry-center-title';
40280                 }
40281
40282                 if(this.bgimage.length){
40283                     cls += ' masonry-bottom-title';
40284                 }
40285                 break;
40286         }
40287         
40288         if(this.cls){
40289             cls += ' ' + this.cls;
40290         }
40291         
40292         var cfg = {
40293             tag: (this.href.length) ? 'a' : 'div',
40294             cls: cls,
40295             cn: [
40296                 {
40297                     tag: 'div',
40298                     cls: 'masonry-brick-split-head',
40299                     cn: [
40300                         {
40301                             tag: 'div',
40302                             cls: 'masonry-brick-paragraph',
40303                             cn: []
40304                         }
40305                     ]
40306                 },
40307                 {
40308                     tag: 'div',
40309                     cls: 'masonry-brick-split-body',
40310                     cn: []
40311                 }
40312             ]
40313         };
40314         
40315         if(this.href.length){
40316             cfg.href = this.href;
40317         }
40318         
40319         if(this.title.length){
40320             cfg.cn[0].cn[0].cn.push({
40321                 tag: 'h4',
40322                 cls: 'masonry-brick-title',
40323                 html: this.title
40324             });
40325         }
40326         
40327         if(this.html.length){
40328             cfg.cn[1].cn.push({
40329                 tag: 'p',
40330                 cls: 'masonry-brick-text',
40331                 html: this.html
40332             });
40333         }
40334
40335         if(this.bgimage.length){
40336             cfg.cn[0].cn.push({
40337                 tag: 'img',
40338                 cls: 'masonry-brick-image-view',
40339                 src: this.bgimage
40340             });
40341         }
40342         
40343         if(this.videourl.length){
40344             var vurl = this.videourl.replace(/https:\/\/youtu\.be/, 'https://www.youtube.com/embed/');
40345             // youtube support only?
40346             cfg.cn[0].cn.cn.push({
40347                 tag: 'iframe',
40348                 cls: 'masonry-brick-image-view',
40349                 src: vurl,
40350                 frameborder : 0,
40351                 allowfullscreen : true
40352             });
40353         }
40354         
40355         return cfg;
40356     },
40357     
40358     initEvents: function() 
40359     {
40360         switch (this.size) {
40361             case 'xs' :
40362                 this.x = 1;
40363                 this.y = 1;
40364                 break;
40365             case 'sm' :
40366                 this.x = 2;
40367                 this.y = 2;
40368                 break;
40369             case 'md' :
40370             case 'md-left' :
40371             case 'md-right' :
40372                 this.x = 3;
40373                 this.y = 3;
40374                 break;
40375             case 'tall' :
40376                 this.x = 2;
40377                 this.y = 3;
40378                 break;
40379             case 'wide' :
40380                 this.x = 3;
40381                 this.y = 2;
40382                 break;
40383             case 'wide-thin' :
40384                 this.x = 3;
40385                 this.y = 1;
40386                 break;
40387                         
40388             default :
40389                 break;
40390         }
40391         
40392         if(Roo.isTouch){
40393             this.el.on('touchstart', this.onTouchStart, this);
40394             this.el.on('touchmove', this.onTouchMove, this);
40395             this.el.on('touchend', this.onTouchEnd, this);
40396             this.el.on('contextmenu', this.onContextMenu, this);
40397         } else {
40398             this.el.on('mouseenter'  ,this.enter, this);
40399             this.el.on('mouseleave', this.leave, this);
40400             this.el.on('click', this.onClick, this);
40401         }
40402         
40403         if (typeof(this.parent().bricks) == 'object' && this.parent().bricks != null) {
40404             this.parent().bricks.push(this);   
40405         }
40406         
40407     },
40408     
40409     onClick: function(e, el)
40410     {
40411         var time = this.endTimer - this.startTimer;
40412         // Roo.log(e.preventDefault());
40413         if(Roo.isTouch){
40414             if(time > 1000){
40415                 e.preventDefault();
40416                 return;
40417             }
40418         }
40419         
40420         if(!this.preventDefault){
40421             return;
40422         }
40423         
40424         e.preventDefault();
40425         
40426         if (this.activeClass != '') {
40427             this.selectBrick();
40428         }
40429         
40430         this.fireEvent('click', this, e);
40431     },
40432     
40433     enter: function(e, el)
40434     {
40435         e.preventDefault();
40436         
40437         if(!this.isFitContainer || this.maskInverse || this.videourl.length){
40438             return;
40439         }
40440         
40441         if(this.bgimage.length && this.html.length){
40442             this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
40443         }
40444     },
40445     
40446     leave: function(e, el)
40447     {
40448         e.preventDefault();
40449         
40450         if(!this.isFitContainer || this.maskInverse  || this.videourl.length){
40451             return;
40452         }
40453         
40454         if(this.bgimage.length && this.html.length){
40455             this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
40456         }
40457     },
40458     
40459     onTouchStart: function(e, el)
40460     {
40461 //        e.preventDefault();
40462         
40463         this.touchmoved = false;
40464         
40465         if(!this.isFitContainer){
40466             return;
40467         }
40468         
40469         if(!this.bgimage.length || !this.html.length){
40470             return;
40471         }
40472         
40473         this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
40474         
40475         this.timer = new Date().getTime();
40476         
40477     },
40478     
40479     onTouchMove: function(e, el)
40480     {
40481         this.touchmoved = true;
40482     },
40483     
40484     onContextMenu : function(e,el)
40485     {
40486         e.preventDefault();
40487         e.stopPropagation();
40488         return false;
40489     },
40490     
40491     onTouchEnd: function(e, el)
40492     {
40493 //        e.preventDefault();
40494         
40495         if((new Date().getTime() - this.timer > 1000) || !this.href.length || this.touchmoved){
40496         
40497             this.leave(e,el);
40498             
40499             return;
40500         }
40501         
40502         if(!this.bgimage.length || !this.html.length){
40503             
40504             if(this.href.length){
40505                 window.location.href = this.href;
40506             }
40507             
40508             return;
40509         }
40510         
40511         if(!this.isFitContainer){
40512             return;
40513         }
40514         
40515         this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
40516         
40517         window.location.href = this.href;
40518     },
40519     
40520     //selection on single brick only
40521     selectBrick : function() {
40522         
40523         if (!this.parentId) {
40524             return;
40525         }
40526         
40527         var m = Roo.bootstrap.LayoutMasonry.get(this.parentId);
40528         var index = m.selectedBrick.indexOf(this.id);
40529         
40530         if ( index > -1) {
40531             m.selectedBrick.splice(index,1);
40532             this.el.removeClass(this.activeClass);
40533             return;
40534         }
40535         
40536         for(var i = 0; i < m.selectedBrick.length; i++) {
40537             var b = Roo.bootstrap.MasonryBrick.get(m.selectedBrick[i]);
40538             b.el.removeClass(b.activeClass);
40539         }
40540         
40541         m.selectedBrick = [];
40542         
40543         m.selectedBrick.push(this.id);
40544         this.el.addClass(this.activeClass);
40545         return;
40546     },
40547     
40548     isSelected : function(){
40549         return this.el.hasClass(this.activeClass);
40550         
40551     }
40552 });
40553
40554 Roo.apply(Roo.bootstrap.MasonryBrick, {
40555     
40556     //groups: {},
40557     groups : new Roo.util.MixedCollection(false, function(o) { return o.el.id; }),
40558      /**
40559     * register a Masonry Brick
40560     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
40561     */
40562     
40563     register : function(brick)
40564     {
40565         //this.groups[brick.id] = brick;
40566         this.groups.add(brick.id, brick);
40567     },
40568     /**
40569     * fetch a  masonry brick based on the masonry brick ID
40570     * @param {string} the masonry brick to add
40571     * @returns {Roo.bootstrap.MasonryBrick} the masonry brick
40572     */
40573     
40574     get: function(brick_id) 
40575     {
40576         // if (typeof(this.groups[brick_id]) == 'undefined') {
40577         //     return false;
40578         // }
40579         // return this.groups[brick_id] ;
40580         
40581         if(this.groups.key(brick_id)) {
40582             return this.groups.key(brick_id);
40583         }
40584         
40585         return false;
40586     }
40587     
40588     
40589     
40590 });
40591
40592  /*
40593  * - LGPL
40594  *
40595  * element
40596  * 
40597  */
40598
40599 /**
40600  * @class Roo.bootstrap.Brick
40601  * @extends Roo.bootstrap.Component
40602  * Bootstrap Brick class
40603  * 
40604  * @constructor
40605  * Create a new Brick
40606  * @param {Object} config The config object
40607  */
40608
40609 Roo.bootstrap.Brick = function(config){
40610     Roo.bootstrap.Brick.superclass.constructor.call(this, config);
40611     
40612     this.addEvents({
40613         // raw events
40614         /**
40615          * @event click
40616          * When a Brick is click
40617          * @param {Roo.bootstrap.Brick} this
40618          * @param {Roo.EventObject} e
40619          */
40620         "click" : true
40621     });
40622 };
40623
40624 Roo.extend(Roo.bootstrap.Brick, Roo.bootstrap.Component,  {
40625     
40626     /**
40627      * @cfg {String} title
40628      */   
40629     title : '',
40630     /**
40631      * @cfg {String} html
40632      */   
40633     html : '',
40634     /**
40635      * @cfg {String} bgimage
40636      */   
40637     bgimage : '',
40638     /**
40639      * @cfg {String} cls
40640      */   
40641     cls : '',
40642     /**
40643      * @cfg {String} href
40644      */   
40645     href : '',
40646     /**
40647      * @cfg {String} video
40648      */   
40649     video : '',
40650     /**
40651      * @cfg {Boolean} square
40652      */   
40653     square : true,
40654     
40655     getAutoCreate : function()
40656     {
40657         var cls = 'roo-brick';
40658         
40659         if(this.href.length){
40660             cls += ' roo-brick-link';
40661         }
40662         
40663         if(this.bgimage.length){
40664             cls += ' roo-brick-image';
40665         }
40666         
40667         if(!this.html.length && !this.bgimage.length){
40668             cls += ' roo-brick-center-title';
40669         }
40670         
40671         if(!this.html.length && this.bgimage.length){
40672             cls += ' roo-brick-bottom-title';
40673         }
40674         
40675         if(this.cls){
40676             cls += ' ' + this.cls;
40677         }
40678         
40679         var cfg = {
40680             tag: (this.href.length) ? 'a' : 'div',
40681             cls: cls,
40682             cn: [
40683                 {
40684                     tag: 'div',
40685                     cls: 'roo-brick-paragraph',
40686                     cn: []
40687                 }
40688             ]
40689         };
40690         
40691         if(this.href.length){
40692             cfg.href = this.href;
40693         }
40694         
40695         var cn = cfg.cn[0].cn;
40696         
40697         if(this.title.length){
40698             cn.push({
40699                 tag: 'h4',
40700                 cls: 'roo-brick-title',
40701                 html: this.title
40702             });
40703         }
40704         
40705         if(this.html.length){
40706             cn.push({
40707                 tag: 'p',
40708                 cls: 'roo-brick-text',
40709                 html: this.html
40710             });
40711         } else {
40712             cn.cls += ' hide';
40713         }
40714         
40715         if(this.bgimage.length){
40716             cfg.cn.push({
40717                 tag: 'img',
40718                 cls: 'roo-brick-image-view',
40719                 src: this.bgimage
40720             });
40721         }
40722         
40723         return cfg;
40724     },
40725     
40726     initEvents: function() 
40727     {
40728         if(this.title.length || this.html.length){
40729             this.el.on('mouseenter'  ,this.enter, this);
40730             this.el.on('mouseleave', this.leave, this);
40731         }
40732         
40733         Roo.EventManager.onWindowResize(this.resize, this); 
40734         
40735         if(this.bgimage.length){
40736             this.imageEl = this.el.select('.roo-brick-image-view', true).first();
40737             this.imageEl.on('load', this.onImageLoad, this);
40738             return;
40739         }
40740         
40741         this.resize();
40742     },
40743     
40744     onImageLoad : function()
40745     {
40746         this.resize();
40747     },
40748     
40749     resize : function()
40750     {
40751         var paragraph = this.el.select('.roo-brick-paragraph', true).first();
40752         
40753         paragraph.setHeight(paragraph.getWidth() + paragraph.getPadding('tb'));
40754         
40755         if(this.bgimage.length){
40756             var image = this.el.select('.roo-brick-image-view', true).first();
40757             
40758             image.setWidth(paragraph.getWidth());
40759             
40760             if(this.square){
40761                 image.setHeight(paragraph.getWidth());
40762             }
40763             
40764             this.el.setHeight(image.getHeight());
40765             paragraph.setHeight(image.getHeight());
40766             
40767         }
40768         
40769     },
40770     
40771     enter: function(e, el)
40772     {
40773         e.preventDefault();
40774         
40775         if(this.bgimage.length){
40776             this.el.select('.roo-brick-paragraph', true).first().setOpacity(0.9, true);
40777             this.el.select('.roo-brick-image-view', true).first().setOpacity(0.1, true);
40778         }
40779     },
40780     
40781     leave: function(e, el)
40782     {
40783         e.preventDefault();
40784         
40785         if(this.bgimage.length){
40786             this.el.select('.roo-brick-paragraph', true).first().setOpacity(0, true);
40787             this.el.select('.roo-brick-image-view', true).first().setOpacity(1, true);
40788         }
40789     }
40790     
40791 });
40792
40793  
40794
40795  /*
40796  * - LGPL
40797  *
40798  * Number field 
40799  */
40800
40801 /**
40802  * @class Roo.bootstrap.form.NumberField
40803  * @extends Roo.bootstrap.form.Input
40804  * Bootstrap NumberField class
40805  * 
40806  * 
40807  * 
40808  * 
40809  * @constructor
40810  * Create a new NumberField
40811  * @param {Object} config The config object
40812  */
40813
40814 Roo.bootstrap.form.NumberField = function(config){
40815     Roo.bootstrap.form.NumberField.superclass.constructor.call(this, config);
40816 };
40817
40818 Roo.extend(Roo.bootstrap.form.NumberField, Roo.bootstrap.form.Input, {
40819     
40820     /**
40821      * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
40822      */
40823     allowDecimals : true,
40824     /**
40825      * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
40826      */
40827     decimalSeparator : ".",
40828     /**
40829      * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
40830      */
40831     decimalPrecision : 2,
40832     /**
40833      * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
40834      */
40835     allowNegative : true,
40836     
40837     /**
40838      * @cfg {Boolean} allowZero False to blank out if the user enters '0' (defaults to true)
40839      */
40840     allowZero: true,
40841     /**
40842      * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
40843      */
40844     minValue : Number.NEGATIVE_INFINITY,
40845     /**
40846      * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
40847      */
40848     maxValue : Number.MAX_VALUE,
40849     /**
40850      * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
40851      */
40852     minText : "The minimum value for this field is {0}",
40853     /**
40854      * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
40855      */
40856     maxText : "The maximum value for this field is {0}",
40857     /**
40858      * @cfg {String} nanText Error text to display if the value is not a valid number.  For example, this can happen
40859      * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
40860      */
40861     nanText : "{0} is not a valid number",
40862     /**
40863      * @cfg {String} thousandsDelimiter Symbol of thousandsDelimiter
40864      */
40865     thousandsDelimiter : false,
40866     /**
40867      * @cfg {String} valueAlign alignment of value
40868      */
40869     valueAlign : "left",
40870
40871     getAutoCreate : function()
40872     {
40873         var hiddenInput = {
40874             tag: 'input',
40875             type: 'hidden',
40876             id: Roo.id(),
40877             cls: 'hidden-number-input'
40878         };
40879         
40880         if (this.name) {
40881             hiddenInput.name = this.name;
40882         }
40883         
40884         this.name = '';
40885         
40886         var cfg = Roo.bootstrap.form.NumberField.superclass.getAutoCreate.call(this);
40887         
40888         this.name = hiddenInput.name;
40889         
40890         if(cfg.cn.length > 0) {
40891             cfg.cn.push(hiddenInput);
40892         }
40893         
40894         return cfg;
40895     },
40896
40897     // private
40898     initEvents : function()
40899     {   
40900         Roo.bootstrap.form.NumberField.superclass.initEvents.call(this);
40901         
40902         var allowed = "0123456789";
40903         
40904         if(this.allowDecimals){
40905             allowed += this.decimalSeparator;
40906         }
40907         
40908         if(this.allowNegative){
40909             allowed += "-";
40910         }
40911         
40912         if(this.thousandsDelimiter) {
40913             allowed += ",";
40914         }
40915         
40916         this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
40917         
40918         var keyPress = function(e){
40919             
40920             var k = e.getKey();
40921             
40922             var c = e.getCharCode();
40923             
40924             if(
40925                     (String.fromCharCode(c) == '.' || String.fromCharCode(c) == '-') &&
40926                     allowed.indexOf(String.fromCharCode(c)) === -1
40927             ){
40928                 e.stopEvent();
40929                 return;
40930             }
40931             
40932             if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
40933                 return;
40934             }
40935             
40936             if(allowed.indexOf(String.fromCharCode(c)) === -1){
40937                 e.stopEvent();
40938             }
40939         };
40940         
40941         this.el.on("keypress", keyPress, this);
40942     },
40943     
40944     validateValue : function(value)
40945     {
40946         
40947         if(!Roo.bootstrap.form.NumberField.superclass.validateValue.call(this, value)){
40948             return false;
40949         }
40950         
40951         var num = this.parseValue(value);
40952         
40953         if(isNaN(num)){
40954             this.markInvalid(String.format(this.nanText, value));
40955             return false;
40956         }
40957         
40958         if(num < this.minValue){
40959             this.markInvalid(String.format(this.minText, this.minValue));
40960             return false;
40961         }
40962         
40963         if(num > this.maxValue){
40964             this.markInvalid(String.format(this.maxText, this.maxValue));
40965             return false;
40966         }
40967         
40968         return true;
40969     },
40970
40971     getValue : function()
40972     {
40973         var v = this.hiddenEl().getValue();
40974         
40975         return this.fixPrecision(this.parseValue(v));
40976     },
40977
40978     parseValue : function(value)
40979     {
40980         if(this.thousandsDelimiter) {
40981             value += "";
40982             r = new RegExp(",", "g");
40983             value = value.replace(r, "");
40984         }
40985         
40986         value = parseFloat(String(value).replace(this.decimalSeparator, "."));
40987         return isNaN(value) ? '' : value;
40988     },
40989
40990     fixPrecision : function(value)
40991     {
40992         if(this.thousandsDelimiter) {
40993             value += "";
40994             r = new RegExp(",", "g");
40995             value = value.replace(r, "");
40996         }
40997         
40998         var nan = isNaN(value);
40999         
41000         if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
41001             return nan ? '' : value;
41002         }
41003         return parseFloat(value).toFixed(this.decimalPrecision);
41004     },
41005
41006     setValue : function(v)
41007     {
41008         v = String(this.fixPrecision(v)).replace(".", this.decimalSeparator);
41009         
41010         this.value = v;
41011         
41012         if(this.rendered){
41013             
41014             this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
41015             
41016             this.inputEl().dom.value = (v == '') ? '' :
41017                 Roo.util.Format.number(v, this.decimalPrecision, this.thousandsDelimiter || '');
41018             
41019             if(!this.allowZero && v === '0') {
41020                 this.hiddenEl().dom.value = '';
41021                 this.inputEl().dom.value = '';
41022             }
41023             
41024             this.validate();
41025         }
41026     },
41027
41028     decimalPrecisionFcn : function(v)
41029     {
41030         return Math.floor(v);
41031     },
41032
41033     beforeBlur : function()
41034     {
41035         var v = this.parseValue(this.getRawValue());
41036         
41037         if(v || v === 0 || v === ''){
41038             this.setValue(v);
41039         }
41040     },
41041     
41042     hiddenEl : function()
41043     {
41044         return this.el.select('input.hidden-number-input',true).first();
41045     }
41046     
41047 });
41048
41049  
41050
41051 /*
41052 * Licence: LGPL
41053 */
41054
41055 /**
41056  * @class Roo.bootstrap.DocumentSlider
41057  * @extends Roo.bootstrap.Component
41058  * Bootstrap DocumentSlider class
41059  * 
41060  * @constructor
41061  * Create a new DocumentViewer
41062  * @param {Object} config The config object
41063  */
41064
41065 Roo.bootstrap.DocumentSlider = function(config){
41066     Roo.bootstrap.DocumentSlider.superclass.constructor.call(this, config);
41067     
41068     this.files = [];
41069     
41070     this.addEvents({
41071         /**
41072          * @event initial
41073          * Fire after initEvent
41074          * @param {Roo.bootstrap.DocumentSlider} this
41075          */
41076         "initial" : true,
41077         /**
41078          * @event update
41079          * Fire after update
41080          * @param {Roo.bootstrap.DocumentSlider} this
41081          */
41082         "update" : true,
41083         /**
41084          * @event click
41085          * Fire after click
41086          * @param {Roo.bootstrap.DocumentSlider} this
41087          */
41088         "click" : true
41089     });
41090 };
41091
41092 Roo.extend(Roo.bootstrap.DocumentSlider, Roo.bootstrap.Component,  {
41093     
41094     files : false,
41095     
41096     indicator : 0,
41097     
41098     getAutoCreate : function()
41099     {
41100         var cfg = {
41101             tag : 'div',
41102             cls : 'roo-document-slider',
41103             cn : [
41104                 {
41105                     tag : 'div',
41106                     cls : 'roo-document-slider-header',
41107                     cn : [
41108                         {
41109                             tag : 'div',
41110                             cls : 'roo-document-slider-header-title'
41111                         }
41112                     ]
41113                 },
41114                 {
41115                     tag : 'div',
41116                     cls : 'roo-document-slider-body',
41117                     cn : [
41118                         {
41119                             tag : 'div',
41120                             cls : 'roo-document-slider-prev',
41121                             cn : [
41122                                 {
41123                                     tag : 'i',
41124                                     cls : 'fa fa-chevron-left'
41125                                 }
41126                             ]
41127                         },
41128                         {
41129                             tag : 'div',
41130                             cls : 'roo-document-slider-thumb',
41131                             cn : [
41132                                 {
41133                                     tag : 'img',
41134                                     cls : 'roo-document-slider-image'
41135                                 }
41136                             ]
41137                         },
41138                         {
41139                             tag : 'div',
41140                             cls : 'roo-document-slider-next',
41141                             cn : [
41142                                 {
41143                                     tag : 'i',
41144                                     cls : 'fa fa-chevron-right'
41145                                 }
41146                             ]
41147                         }
41148                     ]
41149                 }
41150             ]
41151         };
41152         
41153         return cfg;
41154     },
41155     
41156     initEvents : function()
41157     {
41158         this.headerEl = this.el.select('.roo-document-slider-header', true).first();
41159         this.headerEl.setVisibilityMode(Roo.Element.DISPLAY);
41160         
41161         this.titleEl = this.el.select('.roo-document-slider-header .roo-document-slider-header-title', true).first();
41162         this.titleEl.setVisibilityMode(Roo.Element.DISPLAY);
41163         
41164         this.bodyEl = this.el.select('.roo-document-slider-body', true).first();
41165         this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY);
41166         
41167         this.thumbEl = this.el.select('.roo-document-slider-thumb', true).first();
41168         this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY);
41169         
41170         this.imageEl = this.el.select('.roo-document-slider-image', true).first();
41171         this.imageEl.setVisibilityMode(Roo.Element.DISPLAY);
41172         
41173         this.prevIndicator = this.el.select('.roo-document-slider-prev i', true).first();
41174         this.prevIndicator.setVisibilityMode(Roo.Element.DISPLAY);
41175         
41176         this.nextIndicator = this.el.select('.roo-document-slider-next i', true).first();
41177         this.nextIndicator.setVisibilityMode(Roo.Element.DISPLAY);
41178         
41179         this.thumbEl.on('click', this.onClick, this);
41180         
41181         this.prevIndicator.on('click', this.prev, this);
41182         
41183         this.nextIndicator.on('click', this.next, this);
41184         
41185     },
41186     
41187     initial : function()
41188     {
41189         if(this.files.length){
41190             this.indicator = 1;
41191             this.update()
41192         }
41193         
41194         this.fireEvent('initial', this);
41195     },
41196     
41197     update : function()
41198     {
41199         this.imageEl.attr('src', this.files[this.indicator - 1]);
41200         
41201         this.titleEl.dom.innerHTML = String.format('{0} / {1}', this.indicator, this.files.length);
41202         
41203         this.prevIndicator.show();
41204         
41205         if(this.indicator == 1){
41206             this.prevIndicator.hide();
41207         }
41208         
41209         this.nextIndicator.show();
41210         
41211         if(this.indicator == this.files.length){
41212             this.nextIndicator.hide();
41213         }
41214         
41215         this.thumbEl.scrollTo('top');
41216         
41217         this.fireEvent('update', this);
41218     },
41219     
41220     onClick : function(e)
41221     {
41222         e.preventDefault();
41223         
41224         this.fireEvent('click', this);
41225     },
41226     
41227     prev : function(e)
41228     {
41229         e.preventDefault();
41230         
41231         this.indicator = Math.max(1, this.indicator - 1);
41232         
41233         this.update();
41234     },
41235     
41236     next : function(e)
41237     {
41238         e.preventDefault();
41239         
41240         this.indicator = Math.min(this.files.length, this.indicator + 1);
41241         
41242         this.update();
41243     }
41244 });
41245 /*
41246  * - LGPL
41247  *
41248  * RadioSet
41249  *
41250  *
41251  */
41252
41253 /**
41254  * @class Roo.bootstrap.form.RadioSet
41255  * @extends Roo.bootstrap.form.Input
41256  * @children Roo.bootstrap.form.Radio
41257  * Bootstrap RadioSet class
41258  * @cfg {String} indicatorpos (left|right) default left
41259  * @cfg {Boolean} inline (true|false) inline the element (default true)
41260  * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the radio
41261  * @constructor
41262  * Create a new RadioSet
41263  * @param {Object} config The config object
41264  */
41265
41266 Roo.bootstrap.form.RadioSet = function(config){
41267     
41268     Roo.bootstrap.form.RadioSet.superclass.constructor.call(this, config);
41269     
41270     this.radioes = [];
41271     
41272     Roo.bootstrap.form.RadioSet.register(this);
41273     
41274     this.addEvents({
41275         /**
41276         * @event check
41277         * Fires when the element is checked or unchecked.
41278         * @param {Roo.bootstrap.form.RadioSet} this This radio
41279         * @param {Roo.bootstrap.form.Radio} item The checked item
41280         */
41281        check : true,
41282        /**
41283         * @event click
41284         * Fires when the element is click.
41285         * @param {Roo.bootstrap.form.RadioSet} this This radio set
41286         * @param {Roo.bootstrap.form.Radio} item The checked item
41287         * @param {Roo.EventObject} e The event object
41288         */
41289        click : true
41290     });
41291     
41292 };
41293
41294 Roo.extend(Roo.bootstrap.form.RadioSet, Roo.bootstrap.form.Input,  {
41295
41296     radioes : false,
41297     
41298     inline : true,
41299     
41300     weight : '',
41301     
41302     indicatorpos : 'left',
41303     
41304     getAutoCreate : function()
41305     {
41306         var label = {
41307             tag : 'label',
41308             cls : 'roo-radio-set-label',
41309             cn : [
41310                 {
41311                     tag : 'span',
41312                     html : this.fieldLabel
41313                 }
41314             ]
41315         };
41316         if (Roo.bootstrap.version == 3) {
41317             
41318             
41319             if(this.indicatorpos == 'left'){
41320                 label.cn.unshift({
41321                     tag : 'i',
41322                     cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
41323                     tooltip : 'This field is required'
41324                 });
41325             } else {
41326                 label.cn.push({
41327                     tag : 'i',
41328                     cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
41329                     tooltip : 'This field is required'
41330                 });
41331             }
41332         }
41333         var items = {
41334             tag : 'div',
41335             cls : 'roo-radio-set-items'
41336         };
41337         
41338         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
41339         
41340         if (align === 'left' && this.fieldLabel.length) {
41341             
41342             items = {
41343                 cls : "roo-radio-set-right", 
41344                 cn: [
41345                     items
41346                 ]
41347             };
41348             
41349             if(this.labelWidth > 12){
41350                 label.style = "width: " + this.labelWidth + 'px';
41351             }
41352             
41353             if(this.labelWidth < 13 && this.labelmd == 0){
41354                 this.labelmd = this.labelWidth;
41355             }
41356             
41357             if(this.labellg > 0){
41358                 label.cls += ' col-lg-' + this.labellg;
41359                 items.cls += ' col-lg-' + (12 - this.labellg);
41360             }
41361             
41362             if(this.labelmd > 0){
41363                 label.cls += ' col-md-' + this.labelmd;
41364                 items.cls += ' col-md-' + (12 - this.labelmd);
41365             }
41366             
41367             if(this.labelsm > 0){
41368                 label.cls += ' col-sm-' + this.labelsm;
41369                 items.cls += ' col-sm-' + (12 - this.labelsm);
41370             }
41371             
41372             if(this.labelxs > 0){
41373                 label.cls += ' col-xs-' + this.labelxs;
41374                 items.cls += ' col-xs-' + (12 - this.labelxs);
41375             }
41376         }
41377         
41378         var cfg = {
41379             tag : 'div',
41380             cls : 'roo-radio-set',
41381             cn : [
41382                 {
41383                     tag : 'input',
41384                     cls : 'roo-radio-set-input',
41385                     type : 'hidden',
41386                     name : this.name,
41387                     value : this.value ? this.value :  ''
41388                 },
41389                 label,
41390                 items
41391             ]
41392         };
41393         
41394         if(this.weight.length){
41395             cfg.cls += ' roo-radio-' + this.weight;
41396         }
41397         
41398         if(this.inline) {
41399             cfg.cls += ' roo-radio-set-inline';
41400         }
41401         
41402         var settings=this;
41403         ['xs','sm','md','lg'].map(function(size){
41404             if (settings[size]) {
41405                 cfg.cls += ' col-' + size + '-' + settings[size];
41406             }
41407         });
41408         
41409         return cfg;
41410         
41411     },
41412
41413     initEvents : function()
41414     {
41415         this.labelEl = this.el.select('.roo-radio-set-label', true).first();
41416         this.labelEl.setVisibilityMode(Roo.Element.DISPLAY);
41417         
41418         if(!this.fieldLabel.length){
41419             this.labelEl.hide();
41420         }
41421         
41422         this.itemsEl = this.el.select('.roo-radio-set-items', true).first();
41423         this.itemsEl.setVisibilityMode(Roo.Element.DISPLAY);
41424         
41425         this.indicator = this.indicatorEl();
41426         
41427         if(this.indicator){
41428             this.indicator.addClass('invisible');
41429         }
41430         
41431         this.originalValue = this.getValue();
41432         
41433     },
41434     
41435     inputEl: function ()
41436     {
41437         return this.el.select('.roo-radio-set-input', true).first();
41438     },
41439     
41440     getChildContainer : function()
41441     {
41442         return this.itemsEl;
41443     },
41444     
41445     register : function(item)
41446     {
41447         this.radioes.push(item);
41448         
41449     },
41450     
41451     validate : function()
41452     {   
41453         if(this.getVisibilityEl().hasClass('hidden')){
41454             return true;
41455         }
41456         
41457         var valid = false;
41458         
41459         Roo.each(this.radioes, function(i){
41460             if(!i.checked){
41461                 return;
41462             }
41463             
41464             valid = true;
41465             return false;
41466         });
41467         
41468         if(this.allowBlank) {
41469             return true;
41470         }
41471         
41472         if(this.disabled || valid){
41473             this.markValid();
41474             return true;
41475         }
41476         
41477         this.markInvalid();
41478         return false;
41479         
41480     },
41481     
41482     markValid : function()
41483     {
41484         if(this.labelEl.isVisible(true) && this.indicatorEl()){
41485             this.indicatorEl().removeClass('visible');
41486             this.indicatorEl().addClass('invisible');
41487         }
41488         
41489         
41490         if (Roo.bootstrap.version == 3) {
41491             this.el.removeClass([this.invalidClass, this.validClass]);
41492             this.el.addClass(this.validClass);
41493         } else {
41494             this.el.removeClass(['is-invalid','is-valid']);
41495             this.el.addClass(['is-valid']);
41496         }
41497         this.fireEvent('valid', this);
41498     },
41499     
41500     markInvalid : function(msg)
41501     {
41502         if(this.allowBlank || this.disabled){
41503             return;
41504         }
41505         
41506         if(this.labelEl.isVisible(true) && this.indicatorEl()){
41507             this.indicatorEl().removeClass('invisible');
41508             this.indicatorEl().addClass('visible');
41509         }
41510         if (Roo.bootstrap.version == 3) {
41511             this.el.removeClass([this.invalidClass, this.validClass]);
41512             this.el.addClass(this.invalidClass);
41513         } else {
41514             this.el.removeClass(['is-invalid','is-valid']);
41515             this.el.addClass(['is-invalid']);
41516         }
41517         
41518         this.fireEvent('invalid', this, msg);
41519         
41520     },
41521     
41522     setValue : function(v, suppressEvent)
41523     {   
41524         if(this.value === v){
41525             return;
41526         }
41527         
41528         this.value = v;
41529         
41530         if(this.rendered){
41531             this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
41532         }
41533         
41534         Roo.each(this.radioes, function(i){
41535             i.checked = false;
41536             i.el.removeClass('checked');
41537         });
41538         
41539         Roo.each(this.radioes, function(i){
41540             
41541             if(i.value === v || i.value.toString() === v.toString()){
41542                 i.checked = true;
41543                 i.el.addClass('checked');
41544                 
41545                 if(suppressEvent !== true){
41546                     this.fireEvent('check', this, i);
41547                 }
41548                 
41549                 return false;
41550             }
41551             
41552         }, this);
41553         
41554         this.validate();
41555     },
41556     
41557     clearInvalid : function(){
41558         
41559         if(!this.el || this.preventMark){
41560             return;
41561         }
41562         
41563         this.el.removeClass([this.invalidClass]);
41564         
41565         this.fireEvent('valid', this);
41566     }
41567     
41568 });
41569
41570 Roo.apply(Roo.bootstrap.form.RadioSet, {
41571     
41572     groups: {},
41573     
41574     register : function(set)
41575     {
41576         this.groups[set.name] = set;
41577     },
41578     
41579     get: function(name) 
41580     {
41581         if (typeof(this.groups[name]) == 'undefined') {
41582             return false;
41583         }
41584         
41585         return this.groups[name] ;
41586     }
41587     
41588 });
41589 /*
41590  * Based on:
41591  * Ext JS Library 1.1.1
41592  * Copyright(c) 2006-2007, Ext JS, LLC.
41593  *
41594  * Originally Released Under LGPL - original licence link has changed is not relivant.
41595  *
41596  * Fork - LGPL
41597  * <script type="text/javascript">
41598  */
41599
41600
41601 /**
41602  * @class Roo.bootstrap.SplitBar
41603  * @extends Roo.util.Observable
41604  * Creates draggable splitter bar functionality from two elements (element to be dragged and element to be resized).
41605  * <br><br>
41606  * Usage:
41607  * <pre><code>
41608 var split = new Roo.bootstrap.SplitBar("elementToDrag", "elementToSize",
41609                    Roo.bootstrap.SplitBar.HORIZONTAL, Roo.bootstrap.SplitBar.LEFT);
41610 split.setAdapter(new Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter("container"));
41611 split.minSize = 100;
41612 split.maxSize = 600;
41613 split.animate = true;
41614 split.on('moved', splitterMoved);
41615 </code></pre>
41616  * @constructor
41617  * Create a new SplitBar
41618  * @config {String/HTMLElement/Roo.Element} dragElement The element to be dragged and act as the SplitBar. 
41619  * @config {String/HTMLElement/Roo.Element} resizingElement The element to be resized based on where the SplitBar element is dragged 
41620  * @config {Number} orientation (optional) Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
41621  * @config {Number} placement (optional) Either Roo.bootstrap.SplitBar.LEFT or Roo.bootstrap.SplitBar.RIGHT for horizontal or  
41622                         Roo.bootstrap.SplitBar.TOP or Roo.bootstrap.SplitBar.BOTTOM for vertical. (By default, this is determined automatically by the initial
41623                         position of the SplitBar).
41624  */
41625 Roo.bootstrap.SplitBar = function(cfg){
41626     
41627     /** @private */
41628     
41629     //{
41630     //  dragElement : elm
41631     //  resizingElement: el,
41632         // optional..
41633     //    orientation : Either Roo.bootstrap.SplitBar.HORIZONTAL
41634     //    placement : Roo.bootstrap.SplitBar.LEFT  ,
41635         // existingProxy ???
41636     //}
41637     
41638     this.el = Roo.get(cfg.dragElement, true);
41639     this.el.dom.unselectable = "on";
41640     /** @private */
41641     this.resizingEl = Roo.get(cfg.resizingElement, true);
41642
41643     /**
41644      * @private
41645      * The orientation of the split. Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
41646      * Note: If this is changed after creating the SplitBar, the placement property must be manually updated
41647      * @type Number
41648      */
41649     this.orientation = cfg.orientation || Roo.bootstrap.SplitBar.HORIZONTAL;
41650     
41651     /**
41652      * The minimum size of the resizing element. (Defaults to 0)
41653      * @type Number
41654      */
41655     this.minSize = 0;
41656     
41657     /**
41658      * The maximum size of the resizing element. (Defaults to 2000)
41659      * @type Number
41660      */
41661     this.maxSize = 2000;
41662     
41663     /**
41664      * Whether to animate the transition to the new size
41665      * @type Boolean
41666      */
41667     this.animate = false;
41668     
41669     /**
41670      * Whether to create a transparent shim that overlays the page when dragging, enables dragging across iframes.
41671      * @type Boolean
41672      */
41673     this.useShim = false;
41674     
41675     /** @private */
41676     this.shim = null;
41677     
41678     if(!cfg.existingProxy){
41679         /** @private */
41680         this.proxy = Roo.bootstrap.SplitBar.createProxy(this.orientation);
41681     }else{
41682         this.proxy = Roo.get(cfg.existingProxy).dom;
41683     }
41684     /** @private */
41685     this.dd = new Roo.dd.DDProxy(this.el.dom.id, "XSplitBars", {dragElId : this.proxy.id});
41686     
41687     /** @private */
41688     this.dd.b4StartDrag = this.onStartProxyDrag.createDelegate(this);
41689     
41690     /** @private */
41691     this.dd.endDrag = this.onEndProxyDrag.createDelegate(this);
41692     
41693     /** @private */
41694     this.dragSpecs = {};
41695     
41696     /**
41697      * @private The adapter to use to positon and resize elements
41698      */
41699     this.adapter = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
41700     this.adapter.init(this);
41701     
41702     if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
41703         /** @private */
41704         this.placement = cfg.placement || (this.el.getX() > this.resizingEl.getX() ? Roo.bootstrap.SplitBar.LEFT : Roo.bootstrap.SplitBar.RIGHT);
41705         this.el.addClass("roo-splitbar-h");
41706     }else{
41707         /** @private */
41708         this.placement = cfg.placement || (this.el.getY() > this.resizingEl.getY() ? Roo.bootstrap.SplitBar.TOP : Roo.bootstrap.SplitBar.BOTTOM);
41709         this.el.addClass("roo-splitbar-v");
41710     }
41711     
41712     this.addEvents({
41713         /**
41714          * @event resize
41715          * Fires when the splitter is moved (alias for {@link #event-moved})
41716          * @param {Roo.bootstrap.SplitBar} this
41717          * @param {Number} newSize the new width or height
41718          */
41719         "resize" : true,
41720         /**
41721          * @event moved
41722          * Fires when the splitter is moved
41723          * @param {Roo.bootstrap.SplitBar} this
41724          * @param {Number} newSize the new width or height
41725          */
41726         "moved" : true,
41727         /**
41728          * @event beforeresize
41729          * Fires before the splitter is dragged
41730          * @param {Roo.bootstrap.SplitBar} this
41731          */
41732         "beforeresize" : true,
41733
41734         "beforeapply" : true
41735     });
41736
41737     Roo.util.Observable.call(this);
41738 };
41739
41740 Roo.extend(Roo.bootstrap.SplitBar, Roo.util.Observable, {
41741     onStartProxyDrag : function(x, y){
41742         this.fireEvent("beforeresize", this);
41743         if(!this.overlay){
41744             var o = Roo.DomHelper.insertFirst(document.body,  {cls: "roo-drag-overlay", html: "&#160;"}, true);
41745             o.unselectable();
41746             o.enableDisplayMode("block");
41747             // all splitbars share the same overlay
41748             Roo.bootstrap.SplitBar.prototype.overlay = o;
41749         }
41750         this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
41751         this.overlay.show();
41752         Roo.get(this.proxy).setDisplayed("block");
41753         var size = this.adapter.getElementSize(this);
41754         this.activeMinSize = this.getMinimumSize();;
41755         this.activeMaxSize = this.getMaximumSize();;
41756         var c1 = size - this.activeMinSize;
41757         var c2 = Math.max(this.activeMaxSize - size, 0);
41758         if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
41759             this.dd.resetConstraints();
41760             this.dd.setXConstraint(
41761                 this.placement == Roo.bootstrap.SplitBar.LEFT ? c1 : c2, 
41762                 this.placement == Roo.bootstrap.SplitBar.LEFT ? c2 : c1
41763             );
41764             this.dd.setYConstraint(0, 0);
41765         }else{
41766             this.dd.resetConstraints();
41767             this.dd.setXConstraint(0, 0);
41768             this.dd.setYConstraint(
41769                 this.placement == Roo.bootstrap.SplitBar.TOP ? c1 : c2, 
41770                 this.placement == Roo.bootstrap.SplitBar.TOP ? c2 : c1
41771             );
41772          }
41773         this.dragSpecs.startSize = size;
41774         this.dragSpecs.startPoint = [x, y];
41775         Roo.dd.DDProxy.prototype.b4StartDrag.call(this.dd, x, y);
41776     },
41777     
41778     /** 
41779      * @private Called after the drag operation by the DDProxy
41780      */
41781     onEndProxyDrag : function(e){
41782         Roo.get(this.proxy).setDisplayed(false);
41783         var endPoint = Roo.lib.Event.getXY(e);
41784         if(this.overlay){
41785             this.overlay.hide();
41786         }
41787         var newSize;
41788         if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
41789             newSize = this.dragSpecs.startSize + 
41790                 (this.placement == Roo.bootstrap.SplitBar.LEFT ?
41791                     endPoint[0] - this.dragSpecs.startPoint[0] :
41792                     this.dragSpecs.startPoint[0] - endPoint[0]
41793                 );
41794         }else{
41795             newSize = this.dragSpecs.startSize + 
41796                 (this.placement == Roo.bootstrap.SplitBar.TOP ?
41797                     endPoint[1] - this.dragSpecs.startPoint[1] :
41798                     this.dragSpecs.startPoint[1] - endPoint[1]
41799                 );
41800         }
41801         newSize = Math.min(Math.max(newSize, this.activeMinSize), this.activeMaxSize);
41802         if(newSize != this.dragSpecs.startSize){
41803             if(this.fireEvent('beforeapply', this, newSize) !== false){
41804                 this.adapter.setElementSize(this, newSize);
41805                 this.fireEvent("moved", this, newSize);
41806                 this.fireEvent("resize", this, newSize);
41807             }
41808         }
41809     },
41810     
41811     /**
41812      * Get the adapter this SplitBar uses
41813      * @return The adapter object
41814      */
41815     getAdapter : function(){
41816         return this.adapter;
41817     },
41818     
41819     /**
41820      * Set the adapter this SplitBar uses
41821      * @param {Object} adapter A SplitBar adapter object
41822      */
41823     setAdapter : function(adapter){
41824         this.adapter = adapter;
41825         this.adapter.init(this);
41826     },
41827     
41828     /**
41829      * Gets the minimum size for the resizing element
41830      * @return {Number} The minimum size
41831      */
41832     getMinimumSize : function(){
41833         return this.minSize;
41834     },
41835     
41836     /**
41837      * Sets the minimum size for the resizing element
41838      * @param {Number} minSize The minimum size
41839      */
41840     setMinimumSize : function(minSize){
41841         this.minSize = minSize;
41842     },
41843     
41844     /**
41845      * Gets the maximum size for the resizing element
41846      * @return {Number} The maximum size
41847      */
41848     getMaximumSize : function(){
41849         return this.maxSize;
41850     },
41851     
41852     /**
41853      * Sets the maximum size for the resizing element
41854      * @param {Number} maxSize The maximum size
41855      */
41856     setMaximumSize : function(maxSize){
41857         this.maxSize = maxSize;
41858     },
41859     
41860     /**
41861      * Sets the initialize size for the resizing element
41862      * @param {Number} size The initial size
41863      */
41864     setCurrentSize : function(size){
41865         var oldAnimate = this.animate;
41866         this.animate = false;
41867         this.adapter.setElementSize(this, size);
41868         this.animate = oldAnimate;
41869     },
41870     
41871     /**
41872      * Destroy this splitbar. 
41873      * @param {Boolean} removeEl True to remove the element
41874      */
41875     destroy : function(removeEl){
41876         if(this.shim){
41877             this.shim.remove();
41878         }
41879         this.dd.unreg();
41880         this.proxy.parentNode.removeChild(this.proxy);
41881         if(removeEl){
41882             this.el.remove();
41883         }
41884     }
41885 });
41886
41887 /**
41888  * @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.
41889  */
41890 Roo.bootstrap.SplitBar.createProxy = function(dir){
41891     var proxy = new Roo.Element(document.createElement("div"));
41892     proxy.unselectable();
41893     var cls = 'roo-splitbar-proxy';
41894     proxy.addClass(cls + ' ' + (dir == Roo.bootstrap.SplitBar.HORIZONTAL ? cls +'-h' : cls + '-v'));
41895     document.body.appendChild(proxy.dom);
41896     return proxy.dom;
41897 };
41898
41899 /** 
41900  * @class Roo.bootstrap.SplitBar.BasicLayoutAdapter
41901  * Default Adapter. It assumes the splitter and resizing element are not positioned
41902  * elements and only gets/sets the width of the element. Generally used for table based layouts.
41903  */
41904 Roo.bootstrap.SplitBar.BasicLayoutAdapter = function(){
41905 };
41906
41907 Roo.bootstrap.SplitBar.BasicLayoutAdapter.prototype = {
41908     // do nothing for now
41909     init : function(s){
41910     
41911     },
41912     /**
41913      * Called before drag operations to get the current size of the resizing element. 
41914      * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
41915      */
41916      getElementSize : function(s){
41917         if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
41918             return s.resizingEl.getWidth();
41919         }else{
41920             return s.resizingEl.getHeight();
41921         }
41922     },
41923     
41924     /**
41925      * Called after drag operations to set the size of the resizing element.
41926      * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
41927      * @param {Number} newSize The new size to set
41928      * @param {Function} onComplete A function to be invoked when resizing is complete
41929      */
41930     setElementSize : function(s, newSize, onComplete){
41931         if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
41932             if(!s.animate){
41933                 s.resizingEl.setWidth(newSize);
41934                 if(onComplete){
41935                     onComplete(s, newSize);
41936                 }
41937             }else{
41938                 s.resizingEl.setWidth(newSize, true, .1, onComplete, 'easeOut');
41939             }
41940         }else{
41941             
41942             if(!s.animate){
41943                 s.resizingEl.setHeight(newSize);
41944                 if(onComplete){
41945                     onComplete(s, newSize);
41946                 }
41947             }else{
41948                 s.resizingEl.setHeight(newSize, true, .1, onComplete, 'easeOut');
41949             }
41950         }
41951     }
41952 };
41953
41954 /** 
41955  *@class Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter
41956  * @extends Roo.bootstrap.SplitBar.BasicLayoutAdapter
41957  * Adapter that  moves the splitter element to align with the resized sizing element. 
41958  * Used with an absolute positioned SplitBar.
41959  * @param {String/HTMLElement/Roo.Element} container The container that wraps around the absolute positioned content. If it's
41960  * document.body, make sure you assign an id to the body element.
41961  */
41962 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter = function(container){
41963     this.basic = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
41964     this.container = Roo.get(container);
41965 };
41966
41967 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter.prototype = {
41968     init : function(s){
41969         this.basic.init(s);
41970     },
41971     
41972     getElementSize : function(s){
41973         return this.basic.getElementSize(s);
41974     },
41975     
41976     setElementSize : function(s, newSize, onComplete){
41977         this.basic.setElementSize(s, newSize, this.moveSplitter.createDelegate(this, [s]));
41978     },
41979     
41980     moveSplitter : function(s){
41981         var yes = Roo.bootstrap.SplitBar;
41982         switch(s.placement){
41983             case yes.LEFT:
41984                 s.el.setX(s.resizingEl.getRight());
41985                 break;
41986             case yes.RIGHT:
41987                 s.el.setStyle("right", (this.container.getWidth() - s.resizingEl.getLeft()) + "px");
41988                 break;
41989             case yes.TOP:
41990                 s.el.setY(s.resizingEl.getBottom());
41991                 break;
41992             case yes.BOTTOM:
41993                 s.el.setY(s.resizingEl.getTop() - s.el.getHeight());
41994                 break;
41995         }
41996     }
41997 };
41998
41999 /**
42000  * Orientation constant - Create a vertical SplitBar
42001  * @static
42002  * @type Number
42003  */
42004 Roo.bootstrap.SplitBar.VERTICAL = 1;
42005
42006 /**
42007  * Orientation constant - Create a horizontal SplitBar
42008  * @static
42009  * @type Number
42010  */
42011 Roo.bootstrap.SplitBar.HORIZONTAL = 2;
42012
42013 /**
42014  * Placement constant - The resizing element is to the left of the splitter element
42015  * @static
42016  * @type Number
42017  */
42018 Roo.bootstrap.SplitBar.LEFT = 1;
42019
42020 /**
42021  * Placement constant - The resizing element is to the right of the splitter element
42022  * @static
42023  * @type Number
42024  */
42025 Roo.bootstrap.SplitBar.RIGHT = 2;
42026
42027 /**
42028  * Placement constant - The resizing element is positioned above the splitter element
42029  * @static
42030  * @type Number
42031  */
42032 Roo.bootstrap.SplitBar.TOP = 3;
42033
42034 /**
42035  * Placement constant - The resizing element is positioned under splitter element
42036  * @static
42037  * @type Number
42038  */
42039 Roo.bootstrap.SplitBar.BOTTOM = 4;
42040 /*
42041  * Based on:
42042  * Ext JS Library 1.1.1
42043  * Copyright(c) 2006-2007, Ext JS, LLC.
42044  *
42045  * Originally Released Under LGPL - original licence link has changed is not relivant.
42046  *
42047  * Fork - LGPL
42048  * <script type="text/javascript">
42049  */
42050
42051 /**
42052  * @class Roo.bootstrap.layout.Manager
42053  * @extends Roo.bootstrap.Component
42054  * @abstract
42055  * Base class for layout managers.
42056  */
42057 Roo.bootstrap.layout.Manager = function(config)
42058 {
42059     this.monitorWindowResize = true; // do this before we apply configuration.
42060     
42061     Roo.bootstrap.layout.Manager.superclass.constructor.call(this,config);
42062
42063
42064
42065
42066
42067     /** false to disable window resize monitoring @type Boolean */
42068     
42069     this.regions = {};
42070     this.addEvents({
42071         /**
42072          * @event layout
42073          * Fires when a layout is performed.
42074          * @param {Roo.LayoutManager} this
42075          */
42076         "layout" : true,
42077         /**
42078          * @event regionresized
42079          * Fires when the user resizes a region.
42080          * @param {Roo.LayoutRegion} region The resized region
42081          * @param {Number} newSize The new size (width for east/west, height for north/south)
42082          */
42083         "regionresized" : true,
42084         /**
42085          * @event regioncollapsed
42086          * Fires when a region is collapsed.
42087          * @param {Roo.LayoutRegion} region The collapsed region
42088          */
42089         "regioncollapsed" : true,
42090         /**
42091          * @event regionexpanded
42092          * Fires when a region is expanded.
42093          * @param {Roo.LayoutRegion} region The expanded region
42094          */
42095         "regionexpanded" : true
42096     });
42097     this.updating = false;
42098
42099     if (config.el) {
42100         this.el = Roo.get(config.el);
42101         this.initEvents();
42102     }
42103
42104 };
42105
42106 Roo.extend(Roo.bootstrap.layout.Manager, Roo.bootstrap.Component, {
42107
42108
42109     regions : null,
42110
42111     monitorWindowResize : true,
42112
42113
42114     updating : false,
42115
42116
42117     onRender : function(ct, position)
42118     {
42119         if(!this.el){
42120             this.el = Roo.get(ct);
42121             this.initEvents();
42122         }
42123         //this.fireEvent('render',this);
42124     },
42125
42126
42127     initEvents: function()
42128     {
42129
42130
42131         // ie scrollbar fix
42132         if(this.el.dom == document.body && Roo.isIE && !config.allowScroll){
42133             document.body.scroll = "no";
42134         }else if(this.el.dom != document.body && this.el.getStyle('position') == 'static'){
42135             this.el.position('relative');
42136         }
42137         this.id = this.el.id;
42138         this.el.addClass("roo-layout-container");
42139         Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
42140         if(this.el.dom != document.body ) {
42141             this.el.on('resize', this.layout,this);
42142             this.el.on('show', this.layout,this);
42143         }
42144
42145     },
42146
42147     /**
42148      * Returns true if this layout is currently being updated
42149      * @return {Boolean}
42150      */
42151     isUpdating : function(){
42152         return this.updating;
42153     },
42154
42155     /**
42156      * Suspend the LayoutManager from doing auto-layouts while
42157      * making multiple add or remove calls
42158      */
42159     beginUpdate : function(){
42160         this.updating = true;
42161     },
42162
42163     /**
42164      * Restore auto-layouts and optionally disable the manager from performing a layout
42165      * @param {Boolean} noLayout true to disable a layout update
42166      */
42167     endUpdate : function(noLayout){
42168         this.updating = false;
42169         if(!noLayout){
42170             this.layout();
42171         }
42172     },
42173
42174     layout: function(){
42175         // abstract...
42176     },
42177
42178     onRegionResized : function(region, newSize){
42179         this.fireEvent("regionresized", region, newSize);
42180         this.layout();
42181     },
42182
42183     onRegionCollapsed : function(region){
42184         this.fireEvent("regioncollapsed", region);
42185     },
42186
42187     onRegionExpanded : function(region){
42188         this.fireEvent("regionexpanded", region);
42189     },
42190
42191     /**
42192      * Returns the size of the current view. This method normalizes document.body and element embedded layouts and
42193      * performs box-model adjustments.
42194      * @return {Object} The size as an object {width: (the width), height: (the height)}
42195      */
42196     getViewSize : function()
42197     {
42198         var size;
42199         if(this.el.dom != document.body){
42200             size = this.el.getSize();
42201         }else{
42202             size = {width: Roo.lib.Dom.getViewWidth(), height: Roo.lib.Dom.getViewHeight()};
42203         }
42204         size.width -= this.el.getBorderWidth("lr")-this.el.getPadding("lr");
42205         size.height -= this.el.getBorderWidth("tb")-this.el.getPadding("tb");
42206         return size;
42207     },
42208
42209     /**
42210      * Returns the Element this layout is bound to.
42211      * @return {Roo.Element}
42212      */
42213     getEl : function(){
42214         return this.el;
42215     },
42216
42217     /**
42218      * Returns the specified region.
42219      * @param {String} target The region key ('center', 'north', 'south', 'east' or 'west')
42220      * @return {Roo.LayoutRegion}
42221      */
42222     getRegion : function(target){
42223         return this.regions[target.toLowerCase()];
42224     },
42225
42226     onWindowResize : function(){
42227         if(this.monitorWindowResize){
42228             this.layout();
42229         }
42230     }
42231 });
42232 /*
42233  * Based on:
42234  * Ext JS Library 1.1.1
42235  * Copyright(c) 2006-2007, Ext JS, LLC.
42236  *
42237  * Originally Released Under LGPL - original licence link has changed is not relivant.
42238  *
42239  * Fork - LGPL
42240  * <script type="text/javascript">
42241  */
42242 /**
42243  * @class Roo.bootstrap.layout.Border
42244  * @extends Roo.bootstrap.layout.Manager
42245  * @children Roo.bootstrap.panel.Content Roo.bootstrap.panel.Nest Roo.bootstrap.panel.Grid
42246  * @parent builder Roo.bootstrap.panel.Nest Roo.bootstrap.panel.Nest Roo.bootstrap.Modal
42247  * This class represents a common layout manager used in desktop applications. For screenshots and more details,
42248  * please see: examples/bootstrap/nested.html<br><br>
42249  
42250 <b>The container the layout is rendered into can be either the body element or any other element.
42251 If it is not the body element, the container needs to either be an absolute positioned element,
42252 or you will need to add "position:relative" to the css of the container.  You will also need to specify
42253 the container size if it is not the body element.</b>
42254
42255 * @constructor
42256 * Create a new Border
42257 * @param {Object} config Configuration options
42258  */
42259 Roo.bootstrap.layout.Border = function(config){
42260     config = config || {};
42261     Roo.bootstrap.layout.Border.superclass.constructor.call(this, config);
42262     
42263     
42264     
42265     Roo.each(Roo.bootstrap.layout.Border.regions, function(region) {
42266         if(config[region]){
42267             config[region].region = region;
42268             this.addRegion(config[region]);
42269         }
42270     },this);
42271     
42272 };
42273
42274 Roo.bootstrap.layout.Border.regions =  ["center", "north","south","east","west"];
42275
42276 Roo.extend(Roo.bootstrap.layout.Border, Roo.bootstrap.layout.Manager, {
42277     
42278         /**
42279          * @cfg {Roo.bootstrap.layout.Region} center region to go in center
42280          */
42281         /**
42282          * @cfg {Roo.bootstrap.layout.Region} west region to go in west
42283          */
42284         /**
42285          * @cfg {Roo.bootstrap.layout.Region} east region to go in east
42286          */
42287         /**
42288          * @cfg {Roo.bootstrap.layout.Region} south region to go in south
42289          */
42290         /**
42291          * @cfg {Roo.bootstrap.layout.Region} north region to go in north
42292          */
42293         
42294         
42295         
42296         
42297     parent : false, // this might point to a 'nest' or a ???
42298     
42299     /**
42300      * Creates and adds a new region if it doesn't already exist.
42301      * @param {String} target The target region key (north, south, east, west or center).
42302      * @param {Object} config The regions config object
42303      * @return {BorderLayoutRegion} The new region
42304      */
42305     addRegion : function(config)
42306     {
42307         if(!this.regions[config.region]){
42308             var r = this.factory(config);
42309             this.bindRegion(r);
42310         }
42311         return this.regions[config.region];
42312     },
42313
42314     // private (kinda)
42315     bindRegion : function(r){
42316         this.regions[r.config.region] = r;
42317         
42318         r.on("visibilitychange",    this.layout, this);
42319         r.on("paneladded",          this.layout, this);
42320         r.on("panelremoved",        this.layout, this);
42321         r.on("invalidated",         this.layout, this);
42322         r.on("resized",             this.onRegionResized, this);
42323         r.on("collapsed",           this.onRegionCollapsed, this);
42324         r.on("expanded",            this.onRegionExpanded, this);
42325     },
42326
42327     /**
42328      * Performs a layout update.
42329      */
42330     layout : function()
42331     {
42332         if(this.updating) {
42333             return;
42334         }
42335         
42336         // render all the rebions if they have not been done alreayd?
42337         Roo.each(Roo.bootstrap.layout.Border.regions, function(region) {
42338             if(this.regions[region] && !this.regions[region].bodyEl){
42339                 this.regions[region].onRender(this.el)
42340             }
42341         },this);
42342         
42343         var size = this.getViewSize();
42344         var w = size.width;
42345         var h = size.height;
42346         var centerW = w;
42347         var centerH = h;
42348         var centerY = 0;
42349         var centerX = 0;
42350         //var x = 0, y = 0;
42351
42352         var rs = this.regions;
42353         var north = rs["north"];
42354         var south = rs["south"]; 
42355         var west = rs["west"];
42356         var east = rs["east"];
42357         var center = rs["center"];
42358         //if(this.hideOnLayout){ // not supported anymore
42359             //c.el.setStyle("display", "none");
42360         //}
42361         if(north && north.isVisible()){
42362             var b = north.getBox();
42363             var m = north.getMargins();
42364             b.width = w - (m.left+m.right);
42365             b.x = m.left;
42366             b.y = m.top;
42367             centerY = b.height + b.y + m.bottom;
42368             centerH -= centerY;
42369             north.updateBox(this.safeBox(b));
42370         }
42371         if(south && south.isVisible()){
42372             var b = south.getBox();
42373             var m = south.getMargins();
42374             b.width = w - (m.left+m.right);
42375             b.x = m.left;
42376             var totalHeight = (b.height + m.top + m.bottom);
42377             b.y = h - totalHeight + m.top;
42378             centerH -= totalHeight;
42379             south.updateBox(this.safeBox(b));
42380         }
42381         if(west && west.isVisible()){
42382             var b = west.getBox();
42383             var m = west.getMargins();
42384             b.height = centerH - (m.top+m.bottom);
42385             b.x = m.left;
42386             b.y = centerY + m.top;
42387             var totalWidth = (b.width + m.left + m.right);
42388             centerX += totalWidth;
42389             centerW -= totalWidth;
42390             west.updateBox(this.safeBox(b));
42391         }
42392         if(east && east.isVisible()){
42393             var b = east.getBox();
42394             var m = east.getMargins();
42395             b.height = centerH - (m.top+m.bottom);
42396             var totalWidth = (b.width + m.left + m.right);
42397             b.x = w - totalWidth + m.left;
42398             b.y = centerY + m.top;
42399             centerW -= totalWidth;
42400             east.updateBox(this.safeBox(b));
42401         }
42402         if(center){
42403             var m = center.getMargins();
42404             var centerBox = {
42405                 x: centerX + m.left,
42406                 y: centerY + m.top,
42407                 width: centerW - (m.left+m.right),
42408                 height: centerH - (m.top+m.bottom)
42409             };
42410             //if(this.hideOnLayout){
42411                 //center.el.setStyle("display", "block");
42412             //}
42413             center.updateBox(this.safeBox(centerBox));
42414         }
42415         this.el.repaint();
42416         this.fireEvent("layout", this);
42417     },
42418
42419     // private
42420     safeBox : function(box){
42421         box.width = Math.max(0, box.width);
42422         box.height = Math.max(0, box.height);
42423         return box;
42424     },
42425
42426     /**
42427      * Adds a ContentPanel (or subclass) to this layout.
42428      * @param {String} target The target region key (north, south, east, west or center).
42429      * @param {Roo.ContentPanel} panel The panel to add
42430      * @return {Roo.ContentPanel} The added panel
42431      */
42432     add : function(target, panel){
42433          
42434         target = target.toLowerCase();
42435         return this.regions[target].add(panel);
42436     },
42437
42438     /**
42439      * Remove a ContentPanel (or subclass) to this layout.
42440      * @param {String} target The target region key (north, south, east, west or center).
42441      * @param {Number/String/Roo.ContentPanel} panel The index, id or panel to remove
42442      * @return {Roo.ContentPanel} The removed panel
42443      */
42444     remove : function(target, panel){
42445         target = target.toLowerCase();
42446         return this.regions[target].remove(panel);
42447     },
42448
42449     /**
42450      * Searches all regions for a panel with the specified id
42451      * @param {String} panelId
42452      * @return {Roo.ContentPanel} The panel or null if it wasn't found
42453      */
42454     findPanel : function(panelId){
42455         var rs = this.regions;
42456         for(var target in rs){
42457             if(typeof rs[target] != "function"){
42458                 var p = rs[target].getPanel(panelId);
42459                 if(p){
42460                     return p;
42461                 }
42462             }
42463         }
42464         return null;
42465     },
42466
42467     /**
42468      * Searches all regions for a panel with the specified id and activates (shows) it.
42469      * @param {String/ContentPanel} panelId The panels id or the panel itself
42470      * @return {Roo.ContentPanel} The shown panel or null
42471      */
42472     showPanel : function(panelId) {
42473       var rs = this.regions;
42474       for(var target in rs){
42475          var r = rs[target];
42476          if(typeof r != "function"){
42477             if(r.hasPanel(panelId)){
42478                return r.showPanel(panelId);
42479             }
42480          }
42481       }
42482       return null;
42483    },
42484
42485    /**
42486      * Restores this layout's state using Roo.state.Manager or the state provided by the passed provider.
42487      * @param {Roo.state.Provider} provider (optional) An alternate state provider
42488      */
42489    /*
42490     restoreState : function(provider){
42491         if(!provider){
42492             provider = Roo.state.Manager;
42493         }
42494         var sm = new Roo.LayoutStateManager();
42495         sm.init(this, provider);
42496     },
42497 */
42498  
42499  
42500     /**
42501      * Adds a xtype elements to the layout.
42502      * <pre><code>
42503
42504 layout.addxtype({
42505        xtype : 'ContentPanel',
42506        region: 'west',
42507        items: [ .... ]
42508    }
42509 );
42510
42511 layout.addxtype({
42512         xtype : 'NestedLayoutPanel',
42513         region: 'west',
42514         layout: {
42515            center: { },
42516            west: { }   
42517         },
42518         items : [ ... list of content panels or nested layout panels.. ]
42519    }
42520 );
42521 </code></pre>
42522      * @param {Object} cfg Xtype definition of item to add.
42523      */
42524     addxtype : function(cfg)
42525     {
42526         // basically accepts a pannel...
42527         // can accept a layout region..!?!?
42528         //Roo.log('Roo.BorderLayout add ' + cfg.xtype)
42529         
42530         
42531         // theory?  children can only be panels??
42532         
42533         //if (!cfg.xtype.match(/Panel$/)) {
42534         //    return false;
42535         //}
42536         var ret = false;
42537         
42538         if (typeof(cfg.region) == 'undefined') {
42539             Roo.log("Failed to add Panel, region was not set");
42540             Roo.log(cfg);
42541             return false;
42542         }
42543         var region = cfg.region;
42544         delete cfg.region;
42545         
42546           
42547         var xitems = [];
42548         if (cfg.items) {
42549             xitems = cfg.items;
42550             delete cfg.items;
42551         }
42552         var nb = false;
42553         
42554         if ( region == 'center') {
42555             Roo.log("Center: " + cfg.title);
42556         }
42557         
42558         
42559         switch(cfg.xtype) 
42560         {
42561             case 'Content':  // ContentPanel (el, cfg)
42562             case 'Scroll':  // ContentPanel (el, cfg)
42563             case 'View': 
42564                 cfg.autoCreate = cfg.autoCreate || true;
42565                 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
42566                 //} else {
42567                 //    var el = this.el.createChild();
42568                 //    ret = new Roo[cfg.xtype](el, cfg); // new panel!!!!!
42569                 //}
42570                 
42571                 this.add(region, ret);
42572                 break;
42573             
42574             /*
42575             case 'TreePanel': // our new panel!
42576                 cfg.el = this.el.createChild();
42577                 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
42578                 this.add(region, ret);
42579                 break;
42580             */
42581             
42582             case 'Nest': 
42583                 // create a new Layout (which is  a Border Layout...
42584                 
42585                 var clayout = cfg.layout;
42586                 clayout.el  = this.el.createChild();
42587                 clayout.items   = clayout.items  || [];
42588                 
42589                 delete cfg.layout;
42590                 
42591                 // replace this exitems with the clayout ones..
42592                 xitems = clayout.items;
42593                  
42594                 // force background off if it's in center...
42595                 if (region == 'center' && this.active && this.getRegion('center').panels.length < 1) {
42596                     cfg.background = false;
42597                 }
42598                 cfg.layout  = new Roo.bootstrap.layout.Border(clayout);
42599                 
42600                 
42601                 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
42602                 //console.log('adding nested layout panel '  + cfg.toSource());
42603                 this.add(region, ret);
42604                 nb = {}; /// find first...
42605                 break;
42606             
42607             case 'Grid':
42608                 
42609                 // needs grid and region
42610                 
42611                 //var el = this.getRegion(region).el.createChild();
42612                 /*
42613                  *var el = this.el.createChild();
42614                 // create the grid first...
42615                 cfg.grid.container = el;
42616                 cfg.grid = new cfg.grid.xns[cfg.grid.xtype](cfg.grid);
42617                 */
42618                 
42619                 if (region == 'center' && this.active ) {
42620                     cfg.background = false;
42621                 }
42622                 
42623                 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
42624                 
42625                 this.add(region, ret);
42626                 /*
42627                 if (cfg.background) {
42628                     // render grid on panel activation (if panel background)
42629                     ret.on('activate', function(gp) {
42630                         if (!gp.grid.rendered) {
42631                     //        gp.grid.render(el);
42632                         }
42633                     });
42634                 } else {
42635                   //  cfg.grid.render(el);
42636                 }
42637                 */
42638                 break;
42639            
42640            
42641             case 'Border': // it can get called on it'self... - might need to check if this is fixed?
42642                 // it was the old xcomponent building that caused this before.
42643                 // espeically if border is the top element in the tree.
42644                 ret = this;
42645                 break; 
42646                 
42647                     
42648                 
42649                 
42650                 
42651             default:
42652                 /*
42653                 if (typeof(Roo[cfg.xtype]) != 'undefined') {
42654                     
42655                     ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
42656                     this.add(region, ret);
42657                 } else {
42658                 */
42659                     Roo.log(cfg);
42660                     throw "Can not add '" + cfg.xtype + "' to Border";
42661                     return null;
42662              
42663                                 
42664              
42665         }
42666         this.beginUpdate();
42667         // add children..
42668         var region = '';
42669         var abn = {};
42670         Roo.each(xitems, function(i)  {
42671             region = nb && i.region ? i.region : false;
42672             
42673             var add = ret.addxtype(i);
42674            
42675             if (region) {
42676                 nb[region] = nb[region] == undefined ? 0 : nb[region]+1;
42677                 if (!i.background) {
42678                     abn[region] = nb[region] ;
42679                 }
42680             }
42681             
42682         });
42683         this.endUpdate();
42684
42685         // make the last non-background panel active..
42686         //if (nb) { Roo.log(abn); }
42687         if (nb) {
42688             
42689             for(var r in abn) {
42690                 region = this.getRegion(r);
42691                 if (region) {
42692                     // tried using nb[r], but it does not work..
42693                      
42694                     region.showPanel(abn[r]);
42695                    
42696                 }
42697             }
42698         }
42699         return ret;
42700         
42701     },
42702     
42703     
42704 // private
42705     factory : function(cfg)
42706     {
42707         
42708         var validRegions = Roo.bootstrap.layout.Border.regions;
42709
42710         var target = cfg.region;
42711         cfg.mgr = this;
42712         
42713         var r = Roo.bootstrap.layout;
42714         Roo.log(target);
42715         switch(target){
42716             case "north":
42717                 return new r.North(cfg);
42718             case "south":
42719                 return new r.South(cfg);
42720             case "east":
42721                 return new r.East(cfg);
42722             case "west":
42723                 return new r.West(cfg);
42724             case "center":
42725                 return new r.Center(cfg);
42726         }
42727         throw 'Layout region "'+target+'" not supported.';
42728     }
42729     
42730     
42731 });
42732  /*
42733  * Based on:
42734  * Ext JS Library 1.1.1
42735  * Copyright(c) 2006-2007, Ext JS, LLC.
42736  *
42737  * Originally Released Under LGPL - original licence link has changed is not relivant.
42738  *
42739  * Fork - LGPL
42740  * <script type="text/javascript">
42741  */
42742  
42743 /**
42744  * @class Roo.bootstrap.layout.Basic
42745  * @extends Roo.util.Observable
42746  * This class represents a lightweight region in a layout manager. This region does not move dom nodes
42747  * and does not have a titlebar, tabs or any other features. All it does is size and position 
42748  * panels. To create a BasicLayoutRegion, add lightweight:true or basic:true to your regions config.
42749  * @cfg {Roo.bootstrap.layout.Manager}   mgr The manager
42750  * @cfg {string}   region  the region that it inhabits..
42751  * @cfg {bool}   skipConfig skip config?
42752  * 
42753
42754  */
42755 Roo.bootstrap.layout.Basic = function(config){
42756     
42757     this.mgr = config.mgr;
42758     
42759     this.position = config.region;
42760     
42761     var skipConfig = config.skipConfig;
42762     
42763     this.events = {
42764         /**
42765          * @scope Roo.BasicLayoutRegion
42766          */
42767         
42768         /**
42769          * @event beforeremove
42770          * Fires before a panel is removed (or closed). To cancel the removal set "e.cancel = true" on the event argument.
42771          * @param {Roo.LayoutRegion} this
42772          * @param {Roo.ContentPanel} panel The panel
42773          * @param {Object} e The cancel event object
42774          */
42775         "beforeremove" : true,
42776         /**
42777          * @event invalidated
42778          * Fires when the layout for this region is changed.
42779          * @param {Roo.LayoutRegion} this
42780          */
42781         "invalidated" : true,
42782         /**
42783          * @event visibilitychange
42784          * Fires when this region is shown or hidden 
42785          * @param {Roo.LayoutRegion} this
42786          * @param {Boolean} visibility true or false
42787          */
42788         "visibilitychange" : true,
42789         /**
42790          * @event paneladded
42791          * Fires when a panel is added. 
42792          * @param {Roo.LayoutRegion} this
42793          * @param {Roo.ContentPanel} panel The panel
42794          */
42795         "paneladded" : true,
42796         /**
42797          * @event panelremoved
42798          * Fires when a panel is removed. 
42799          * @param {Roo.LayoutRegion} this
42800          * @param {Roo.ContentPanel} panel The panel
42801          */
42802         "panelremoved" : true,
42803         /**
42804          * @event beforecollapse
42805          * Fires when this region before collapse.
42806          * @param {Roo.LayoutRegion} this
42807          */
42808         "beforecollapse" : true,
42809         /**
42810          * @event collapsed
42811          * Fires when this region is collapsed.
42812          * @param {Roo.LayoutRegion} this
42813          */
42814         "collapsed" : true,
42815         /**
42816          * @event expanded
42817          * Fires when this region is expanded.
42818          * @param {Roo.LayoutRegion} this
42819          */
42820         "expanded" : true,
42821         /**
42822          * @event slideshow
42823          * Fires when this region is slid into view.
42824          * @param {Roo.LayoutRegion} this
42825          */
42826         "slideshow" : true,
42827         /**
42828          * @event slidehide
42829          * Fires when this region slides out of view. 
42830          * @param {Roo.LayoutRegion} this
42831          */
42832         "slidehide" : true,
42833         /**
42834          * @event panelactivated
42835          * Fires when a panel is activated. 
42836          * @param {Roo.LayoutRegion} this
42837          * @param {Roo.ContentPanel} panel The activated panel
42838          */
42839         "panelactivated" : true,
42840         /**
42841          * @event resized
42842          * Fires when the user resizes this region. 
42843          * @param {Roo.LayoutRegion} this
42844          * @param {Number} newSize The new size (width for east/west, height for north/south)
42845          */
42846         "resized" : true
42847     };
42848     /** A collection of panels in this region. @type Roo.util.MixedCollection */
42849     this.panels = new Roo.util.MixedCollection();
42850     this.panels.getKey = this.getPanelId.createDelegate(this);
42851     this.box = null;
42852     this.activePanel = null;
42853     // ensure listeners are added...
42854     
42855     if (config.listeners || config.events) {
42856         Roo.bootstrap.layout.Basic.superclass.constructor.call(this, {
42857             listeners : config.listeners || {},
42858             events : config.events || {}
42859         });
42860     }
42861     
42862     if(skipConfig !== true){
42863         this.applyConfig(config);
42864     }
42865 };
42866
42867 Roo.extend(Roo.bootstrap.layout.Basic, Roo.util.Observable,
42868 {
42869     getPanelId : function(p){
42870         return p.getId();
42871     },
42872     
42873     applyConfig : function(config){
42874         this.margins = config.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
42875         this.config = config;
42876         
42877     },
42878     
42879     /**
42880      * Resizes the region to the specified size. For vertical regions (west, east) this adjusts 
42881      * the width, for horizontal (north, south) the height.
42882      * @param {Number} newSize The new width or height
42883      */
42884     resizeTo : function(newSize){
42885         var el = this.el ? this.el :
42886                  (this.activePanel ? this.activePanel.getEl() : null);
42887         if(el){
42888             switch(this.position){
42889                 case "east":
42890                 case "west":
42891                     el.setWidth(newSize);
42892                     this.fireEvent("resized", this, newSize);
42893                 break;
42894                 case "north":
42895                 case "south":
42896                     el.setHeight(newSize);
42897                     this.fireEvent("resized", this, newSize);
42898                 break;                
42899             }
42900         }
42901     },
42902     
42903     getBox : function(){
42904         return this.activePanel ? this.activePanel.getEl().getBox(false, true) : null;
42905     },
42906     
42907     getMargins : function(){
42908         return this.margins;
42909     },
42910     
42911     updateBox : function(box){
42912         this.box = box;
42913         var el = this.activePanel.getEl();
42914         el.dom.style.left = box.x + "px";
42915         el.dom.style.top = box.y + "px";
42916         this.activePanel.setSize(box.width, box.height);
42917     },
42918     
42919     /**
42920      * Returns the container element for this region.
42921      * @return {Roo.Element}
42922      */
42923     getEl : function(){
42924         return this.activePanel;
42925     },
42926     
42927     /**
42928      * Returns true if this region is currently visible.
42929      * @return {Boolean}
42930      */
42931     isVisible : function(){
42932         return this.activePanel ? true : false;
42933     },
42934     
42935     setActivePanel : function(panel){
42936         panel = this.getPanel(panel);
42937         if(this.activePanel && this.activePanel != panel){
42938             this.activePanel.setActiveState(false);
42939             this.activePanel.getEl().setLeftTop(-10000,-10000);
42940         }
42941         this.activePanel = panel;
42942         panel.setActiveState(true);
42943         if(this.box){
42944             panel.setSize(this.box.width, this.box.height);
42945         }
42946         this.fireEvent("panelactivated", this, panel);
42947         this.fireEvent("invalidated");
42948     },
42949     
42950     /**
42951      * Show the specified panel.
42952      * @param {Number/String/ContentPanel} panelId The panels index, id or the panel itself
42953      * @return {Roo.ContentPanel} The shown panel or null
42954      */
42955     showPanel : function(panel){
42956         panel = this.getPanel(panel);
42957         if(panel){
42958             this.setActivePanel(panel);
42959         }
42960         return panel;
42961     },
42962     
42963     /**
42964      * Get the active panel for this region.
42965      * @return {Roo.ContentPanel} The active panel or null
42966      */
42967     getActivePanel : function(){
42968         return this.activePanel;
42969     },
42970     
42971     /**
42972      * Add the passed ContentPanel(s)
42973      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
42974      * @return {Roo.ContentPanel} The panel added (if only one was added)
42975      */
42976     add : function(panel){
42977         if(arguments.length > 1){
42978             for(var i = 0, len = arguments.length; i < len; i++) {
42979                 this.add(arguments[i]);
42980             }
42981             return null;
42982         }
42983         if(this.hasPanel(panel)){
42984             this.showPanel(panel);
42985             return panel;
42986         }
42987         var el = panel.getEl();
42988         if(el.dom.parentNode != this.mgr.el.dom){
42989             this.mgr.el.dom.appendChild(el.dom);
42990         }
42991         if(panel.setRegion){
42992             panel.setRegion(this);
42993         }
42994         this.panels.add(panel);
42995         el.setStyle("position", "absolute");
42996         if(!panel.background){
42997             this.setActivePanel(panel);
42998             if(this.config.initialSize && this.panels.getCount()==1){
42999                 this.resizeTo(this.config.initialSize);
43000             }
43001         }
43002         this.fireEvent("paneladded", this, panel);
43003         return panel;
43004     },
43005     
43006     /**
43007      * Returns true if the panel is in this region.
43008      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
43009      * @return {Boolean}
43010      */
43011     hasPanel : function(panel){
43012         if(typeof panel == "object"){ // must be panel obj
43013             panel = panel.getId();
43014         }
43015         return this.getPanel(panel) ? true : false;
43016     },
43017     
43018     /**
43019      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
43020      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
43021      * @param {Boolean} preservePanel Overrides the config preservePanel option
43022      * @return {Roo.ContentPanel} The panel that was removed
43023      */
43024     remove : function(panel, preservePanel){
43025         panel = this.getPanel(panel);
43026         if(!panel){
43027             return null;
43028         }
43029         var e = {};
43030         this.fireEvent("beforeremove", this, panel, e);
43031         if(e.cancel === true){
43032             return null;
43033         }
43034         var panelId = panel.getId();
43035         this.panels.removeKey(panelId);
43036         return panel;
43037     },
43038     
43039     /**
43040      * Returns the panel specified or null if it's not in this region.
43041      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
43042      * @return {Roo.ContentPanel}
43043      */
43044     getPanel : function(id){
43045         if(typeof id == "object"){ // must be panel obj
43046             return id;
43047         }
43048         return this.panels.get(id);
43049     },
43050     
43051     /**
43052      * Returns this regions position (north/south/east/west/center).
43053      * @return {String} 
43054      */
43055     getPosition: function(){
43056         return this.position;    
43057     }
43058 });/*
43059  * Based on:
43060  * Ext JS Library 1.1.1
43061  * Copyright(c) 2006-2007, Ext JS, LLC.
43062  *
43063  * Originally Released Under LGPL - original licence link has changed is not relivant.
43064  *
43065  * Fork - LGPL
43066  * <script type="text/javascript">
43067  */
43068  
43069 /**
43070  * @class Roo.bootstrap.layout.Region
43071  * @extends Roo.bootstrap.layout.Basic
43072  * This class represents a region in a layout manager.
43073  
43074  * @cfg {Object}    margins         Margins for the element (defaults to {top: 0, left: 0, right:0, bottom: 0})
43075  * @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})
43076  * @cfg {String}    tabPosition     (top|bottom) "top" or "bottom" (defaults to "bottom")
43077  * @cfg {Boolean}   alwaysShowTabs  True to always display tabs even when there is only 1 panel (defaults to false)
43078  * @cfg {Boolean}   autoScroll      True to enable overflow scrolling (defaults to false)
43079  * @cfg {Boolean}   titlebar        True to display a title bar (defaults to true)
43080  * @cfg {String}    title           The title for the region (overrides panel titles)
43081  * @cfg {Boolean}   animate         True to animate expand/collapse (defaults to false)
43082  * @cfg {Boolean}   autoHide        False to disable auto hiding when the mouse leaves the "floated" region (defaults to true)
43083  * @cfg {Boolean}   preservePanels  True to preserve removed panels so they can be readded later (defaults to false)
43084  * @cfg {Boolean}   closeOnTab      True to place the close icon on the tabs instead of the region titlebar (defaults to false)
43085  * @cfg {Boolean}   hideTabs        True to hide the tab strip (defaults to false)
43086  * @cfg {Boolean}   resizeTabs      True to enable automatic tab resizing. This will resize the tabs so they are all the same size and fit within
43087  *                      the space available, similar to FireFox 1.5 tabs (defaults to false)
43088  * @cfg {Number}    minTabWidth     The minimum tab width (defaults to 40)
43089  * @cfg {Number}    preferredTabWidth The preferred tab width (defaults to 150)
43090  * @cfg {String}    overflow       (hidden|visible) if you have menus in the region, then you need to set this to visible.
43091
43092  * @cfg {Boolean}   hidden          True to start the region hidden (defaults to false)
43093  * @cfg {Boolean}   hideWhenEmpty   True to hide the region when it has no panels
43094  * @cfg {Boolean}   disableTabTips  True to disable tab tooltips
43095  * @cfg {Number}    width           For East/West panels
43096  * @cfg {Number}    height          For North/South panels
43097  * @cfg {Boolean}   split           To show the splitter
43098  * @cfg {Boolean}   toolbar         xtype configuration for a toolbar - shows on right of tabbar
43099  * 
43100  * @cfg {string}   cls             Extra CSS classes to add to region
43101  * 
43102  * @cfg {Roo.bootstrap.layout.Manager}   mgr The manager
43103  * @cfg {string}   region  the region that it inhabits..
43104  *
43105
43106  * @xxxcfg {Boolean}   collapsible     DISABLED False to disable collapsing (defaults to true)
43107  * @xxxcfg {Boolean}   collapsed       DISABLED True to set the initial display to collapsed (defaults to false)
43108
43109  * @xxxcfg {String}    collapsedTitle  DISABLED Optional string message to display in the collapsed block of a north or south region
43110  * @xxxxcfg {Boolean}   floatable       DISABLED False to disable floating (defaults to true)
43111  * @xxxxcfg {Boolean}   showPin         True to show a pin button NOT SUPPORTED YET
43112  */
43113 Roo.bootstrap.layout.Region = function(config)
43114 {
43115     this.applyConfig(config);
43116
43117     var mgr = config.mgr;
43118     var pos = config.region;
43119     config.skipConfig = true;
43120     Roo.bootstrap.layout.Region.superclass.constructor.call(this, config);
43121     
43122     if (mgr.el) {
43123         this.onRender(mgr.el);   
43124     }
43125      
43126     this.visible = true;
43127     this.collapsed = false;
43128     this.unrendered_panels = [];
43129 };
43130
43131 Roo.extend(Roo.bootstrap.layout.Region, Roo.bootstrap.layout.Basic, {
43132
43133     position: '', // set by wrapper (eg. north/south etc..)
43134     unrendered_panels : null,  // unrendered panels.
43135     
43136     tabPosition : false,
43137     
43138     mgr: false, // points to 'Border'
43139     
43140     
43141     createBody : function(){
43142         /** This region's body element 
43143         * @type Roo.Element */
43144         this.bodyEl = this.el.createChild({
43145                 tag: "div",
43146                 cls: "roo-layout-panel-body tab-content" // bootstrap added...
43147         });
43148     },
43149
43150     onRender: function(ctr, pos)
43151     {
43152         var dh = Roo.DomHelper;
43153         /** This region's container element 
43154         * @type Roo.Element */
43155         this.el = dh.append(ctr.dom, {
43156                 tag: "div",
43157                 cls: (this.config.cls || '') + " roo-layout-region roo-layout-panel roo-layout-panel-" + this.position
43158             }, true);
43159         /** This region's title element 
43160         * @type Roo.Element */
43161     
43162         this.titleEl = dh.append(this.el.dom,  {
43163                 tag: "div",
43164                 unselectable: "on",
43165                 cls: "roo-unselectable roo-layout-panel-hd breadcrumb roo-layout-title-" + this.position,
43166                 children:[
43167                     {tag: "span", cls: "roo-unselectable roo-layout-panel-hd-text", unselectable: "on", html: "&#160;"},
43168                     {tag: "div", cls: "roo-unselectable roo-layout-panel-hd-tools", unselectable: "on"}
43169                 ]
43170             }, true);
43171         
43172         this.titleEl.enableDisplayMode();
43173         /** This region's title text element 
43174         * @type HTMLElement */
43175         this.titleTextEl = this.titleEl.dom.firstChild;
43176         this.tools = Roo.get(this.titleEl.dom.childNodes[1], true);
43177         /*
43178         this.closeBtn = this.createTool(this.tools.dom, "roo-layout-close");
43179         this.closeBtn.enableDisplayMode();
43180         this.closeBtn.on("click", this.closeClicked, this);
43181         this.closeBtn.hide();
43182     */
43183         this.createBody(this.config);
43184         if(this.config.hideWhenEmpty){
43185             this.hide();
43186             this.on("paneladded", this.validateVisibility, this);
43187             this.on("panelremoved", this.validateVisibility, this);
43188         }
43189         if(this.autoScroll){
43190             this.bodyEl.setStyle("overflow", "auto");
43191         }else{
43192             this.bodyEl.setStyle("overflow", this.config.overflow || 'hidden');
43193         }
43194         //if(c.titlebar !== false){
43195             if((!this.config.titlebar && !this.config.title) || this.config.titlebar === false){
43196                 this.titleEl.hide();
43197             }else{
43198                 this.titleEl.show();
43199                 if(this.config.title){
43200                     this.titleTextEl.innerHTML = this.config.title;
43201                 }
43202             }
43203         //}
43204         if(this.config.collapsed){
43205             this.collapse(true);
43206         }
43207         if(this.config.hidden){
43208             this.hide();
43209         }
43210         
43211         if (this.unrendered_panels && this.unrendered_panels.length) {
43212             for (var i =0;i< this.unrendered_panels.length; i++) {
43213                 this.add(this.unrendered_panels[i]);
43214             }
43215             this.unrendered_panels = null;
43216             
43217         }
43218         
43219     },
43220     
43221     applyConfig : function(c)
43222     {
43223         /*
43224          *if(c.collapsible && this.position != "center" && !this.collapsedEl){
43225             var dh = Roo.DomHelper;
43226             if(c.titlebar !== false){
43227                 this.collapseBtn = this.createTool(this.tools.dom, "roo-layout-collapse-"+this.position);
43228                 this.collapseBtn.on("click", this.collapse, this);
43229                 this.collapseBtn.enableDisplayMode();
43230                 /*
43231                 if(c.showPin === true || this.showPin){
43232                     this.stickBtn = this.createTool(this.tools.dom, "roo-layout-stick");
43233                     this.stickBtn.enableDisplayMode();
43234                     this.stickBtn.on("click", this.expand, this);
43235                     this.stickBtn.hide();
43236                 }
43237                 
43238             }
43239             */
43240             /** This region's collapsed element
43241             * @type Roo.Element */
43242             /*
43243              *
43244             this.collapsedEl = dh.append(this.mgr.el.dom, {cls: "x-layout-collapsed x-layout-collapsed-"+this.position, children:[
43245                 {cls: "x-layout-collapsed-tools", children:[{cls: "x-layout-ctools-inner"}]}
43246             ]}, true);
43247             
43248             if(c.floatable !== false){
43249                this.collapsedEl.addClassOnOver("x-layout-collapsed-over");
43250                this.collapsedEl.on("click", this.collapseClick, this);
43251             }
43252
43253             if(c.collapsedTitle && (this.position == "north" || this.position== "south")) {
43254                 this.collapsedTitleTextEl = dh.append(this.collapsedEl.dom, {tag: "div", cls: "x-unselectable x-layout-panel-hd-text",
43255                    id: "message", unselectable: "on", style:{"float":"left"}});
43256                this.collapsedTitleTextEl.innerHTML = c.collapsedTitle;
43257              }
43258             this.expandBtn = this.createTool(this.collapsedEl.dom.firstChild.firstChild, "x-layout-expand-"+this.position);
43259             this.expandBtn.on("click", this.expand, this);
43260             
43261         }
43262         
43263         if(this.collapseBtn){
43264             this.collapseBtn.setVisible(c.collapsible == true);
43265         }
43266         
43267         this.cmargins = c.cmargins || this.cmargins ||
43268                          (this.position == "west" || this.position == "east" ?
43269                              {top: 0, left: 2, right:2, bottom: 0} :
43270                              {top: 2, left: 0, right:0, bottom: 2});
43271         */
43272         this.margins = c.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
43273         
43274         
43275         this.tabPosition = [ 'top','bottom', 'west'].indexOf(c.tabPosition) > -1 ? c.tabPosition : "top";
43276         
43277         this.autoScroll = c.autoScroll || false;
43278         
43279         
43280        
43281         
43282         this.duration = c.duration || .30;
43283         this.slideDuration = c.slideDuration || .45;
43284         this.config = c;
43285        
43286     },
43287     /**
43288      * Returns true if this region is currently visible.
43289      * @return {Boolean}
43290      */
43291     isVisible : function(){
43292         return this.visible;
43293     },
43294
43295     /**
43296      * Updates the title for collapsed north/south regions (used with {@link #collapsedTitle} config option)
43297      * @param {String} title (optional) The title text (accepts HTML markup, defaults to the numeric character reference for a non-breaking space, "&amp;#160;")
43298      */
43299     //setCollapsedTitle : function(title){
43300     //    title = title || "&#160;";
43301      //   if(this.collapsedTitleTextEl){
43302       //      this.collapsedTitleTextEl.innerHTML = title;
43303        // }
43304     //},
43305
43306     getBox : function(){
43307         var b;
43308       //  if(!this.collapsed){
43309             b = this.el.getBox(false, true);
43310        // }else{
43311           //  b = this.collapsedEl.getBox(false, true);
43312         //}
43313         return b;
43314     },
43315
43316     getMargins : function(){
43317         return this.margins;
43318         //return this.collapsed ? this.cmargins : this.margins;
43319     },
43320 /*
43321     highlight : function(){
43322         this.el.addClass("x-layout-panel-dragover");
43323     },
43324
43325     unhighlight : function(){
43326         this.el.removeClass("x-layout-panel-dragover");
43327     },
43328 */
43329     updateBox : function(box)
43330     {
43331         if (!this.bodyEl) {
43332             return; // not rendered yet..
43333         }
43334         
43335         this.box = box;
43336         if(!this.collapsed){
43337             this.el.dom.style.left = box.x + "px";
43338             this.el.dom.style.top = box.y + "px";
43339             this.updateBody(box.width, box.height);
43340         }else{
43341             this.collapsedEl.dom.style.left = box.x + "px";
43342             this.collapsedEl.dom.style.top = box.y + "px";
43343             this.collapsedEl.setSize(box.width, box.height);
43344         }
43345         if(this.tabs){
43346             this.tabs.autoSizeTabs();
43347         }
43348     },
43349
43350     updateBody : function(w, h)
43351     {
43352         if(w !== null){
43353             this.el.setWidth(w);
43354             w -= this.el.getBorderWidth("rl");
43355             if(this.config.adjustments){
43356                 w += this.config.adjustments[0];
43357             }
43358         }
43359         if(h !== null && h > 0){
43360             this.el.setHeight(h);
43361             h = this.titleEl && this.titleEl.isDisplayed() ? h - (this.titleEl.getHeight()||0) : h;
43362             h -= this.el.getBorderWidth("tb");
43363             if(this.config.adjustments){
43364                 h += this.config.adjustments[1];
43365             }
43366             this.bodyEl.setHeight(h);
43367             if(this.tabs){
43368                 h = this.tabs.syncHeight(h);
43369             }
43370         }
43371         if(this.panelSize){
43372             w = w !== null ? w : this.panelSize.width;
43373             h = h !== null ? h : this.panelSize.height;
43374         }
43375         if(this.activePanel){
43376             var el = this.activePanel.getEl();
43377             w = w !== null ? w : el.getWidth();
43378             h = h !== null ? h : el.getHeight();
43379             this.panelSize = {width: w, height: h};
43380             this.activePanel.setSize(w, h);
43381         }
43382         if(Roo.isIE && this.tabs){
43383             this.tabs.el.repaint();
43384         }
43385     },
43386
43387     /**
43388      * Returns the container element for this region.
43389      * @return {Roo.Element}
43390      */
43391     getEl : function(){
43392         return this.el;
43393     },
43394
43395     /**
43396      * Hides this region.
43397      */
43398     hide : function(){
43399         //if(!this.collapsed){
43400             this.el.dom.style.left = "-2000px";
43401             this.el.hide();
43402         //}else{
43403          //   this.collapsedEl.dom.style.left = "-2000px";
43404          //   this.collapsedEl.hide();
43405        // }
43406         this.visible = false;
43407         this.fireEvent("visibilitychange", this, false);
43408     },
43409
43410     /**
43411      * Shows this region if it was previously hidden.
43412      */
43413     show : function(){
43414         //if(!this.collapsed){
43415             this.el.show();
43416         //}else{
43417         //    this.collapsedEl.show();
43418        // }
43419         this.visible = true;
43420         this.fireEvent("visibilitychange", this, true);
43421     },
43422 /*
43423     closeClicked : function(){
43424         if(this.activePanel){
43425             this.remove(this.activePanel);
43426         }
43427     },
43428
43429     collapseClick : function(e){
43430         if(this.isSlid){
43431            e.stopPropagation();
43432            this.slideIn();
43433         }else{
43434            e.stopPropagation();
43435            this.slideOut();
43436         }
43437     },
43438 */
43439     /**
43440      * Collapses this region.
43441      * @param {Boolean} skipAnim (optional) true to collapse the element without animation (if animate is true)
43442      */
43443     /*
43444     collapse : function(skipAnim, skipCheck = false){
43445         if(this.collapsed) {
43446             return;
43447         }
43448         
43449         if(skipCheck || this.fireEvent("beforecollapse", this) != false){
43450             
43451             this.collapsed = true;
43452             if(this.split){
43453                 this.split.el.hide();
43454             }
43455             if(this.config.animate && skipAnim !== true){
43456                 this.fireEvent("invalidated", this);
43457                 this.animateCollapse();
43458             }else{
43459                 this.el.setLocation(-20000,-20000);
43460                 this.el.hide();
43461                 this.collapsedEl.show();
43462                 this.fireEvent("collapsed", this);
43463                 this.fireEvent("invalidated", this);
43464             }
43465         }
43466         
43467     },
43468 */
43469     animateCollapse : function(){
43470         // overridden
43471     },
43472
43473     /**
43474      * Expands this region if it was previously collapsed.
43475      * @param {Roo.EventObject} e The event that triggered the expand (or null if calling manually)
43476      * @param {Boolean} skipAnim (optional) true to expand the element without animation (if animate is true)
43477      */
43478     /*
43479     expand : function(e, skipAnim){
43480         if(e) {
43481             e.stopPropagation();
43482         }
43483         if(!this.collapsed || this.el.hasActiveFx()) {
43484             return;
43485         }
43486         if(this.isSlid){
43487             this.afterSlideIn();
43488             skipAnim = true;
43489         }
43490         this.collapsed = false;
43491         if(this.config.animate && skipAnim !== true){
43492             this.animateExpand();
43493         }else{
43494             this.el.show();
43495             if(this.split){
43496                 this.split.el.show();
43497             }
43498             this.collapsedEl.setLocation(-2000,-2000);
43499             this.collapsedEl.hide();
43500             this.fireEvent("invalidated", this);
43501             this.fireEvent("expanded", this);
43502         }
43503     },
43504 */
43505     animateExpand : function(){
43506         // overridden
43507     },
43508
43509     initTabs : function()
43510     {
43511         //this.bodyEl.setStyle("overflow", "hidden"); -- this is set in render?
43512         
43513         var ts = new Roo.bootstrap.panel.Tabs({
43514             el: this.bodyEl.dom,
43515             region : this,
43516             tabPosition: this.tabPosition ? this.tabPosition  : 'top',
43517             disableTooltips: this.config.disableTabTips,
43518             toolbar : this.config.toolbar
43519         });
43520         
43521         if(this.config.hideTabs){
43522             ts.stripWrap.setDisplayed(false);
43523         }
43524         this.tabs = ts;
43525         ts.resizeTabs = this.config.resizeTabs === true;
43526         ts.minTabWidth = this.config.minTabWidth || 40;
43527         ts.maxTabWidth = this.config.maxTabWidth || 250;
43528         ts.preferredTabWidth = this.config.preferredTabWidth || 150;
43529         ts.monitorResize = false;
43530         //ts.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden"); // this is set in render?
43531         ts.bodyEl.addClass('roo-layout-tabs-body');
43532         this.panels.each(this.initPanelAsTab, this);
43533     },
43534
43535     initPanelAsTab : function(panel){
43536         var ti = this.tabs.addTab(
43537             panel.getEl().id,
43538             panel.getTitle(),
43539             null,
43540             this.config.closeOnTab && panel.isClosable(),
43541             panel.tpl
43542         );
43543         if(panel.tabTip !== undefined){
43544             ti.setTooltip(panel.tabTip);
43545         }
43546         ti.on("activate", function(){
43547               this.setActivePanel(panel);
43548         }, this);
43549         
43550         if(this.config.closeOnTab){
43551             ti.on("beforeclose", function(t, e){
43552                 e.cancel = true;
43553                 this.remove(panel);
43554             }, this);
43555         }
43556         
43557         panel.tabItem = ti;
43558         
43559         return ti;
43560     },
43561
43562     updatePanelTitle : function(panel, title)
43563     {
43564         if(this.activePanel == panel){
43565             this.updateTitle(title);
43566         }
43567         if(this.tabs){
43568             var ti = this.tabs.getTab(panel.getEl().id);
43569             ti.setText(title);
43570             if(panel.tabTip !== undefined){
43571                 ti.setTooltip(panel.tabTip);
43572             }
43573         }
43574     },
43575
43576     updateTitle : function(title){
43577         if(this.titleTextEl && !this.config.title){
43578             this.titleTextEl.innerHTML = (typeof title != "undefined" && title.length > 0 ? title : "&#160;");
43579         }
43580     },
43581
43582     setActivePanel : function(panel)
43583     {
43584         panel = this.getPanel(panel);
43585         if(this.activePanel && this.activePanel != panel){
43586             if(this.activePanel.setActiveState(false) === false){
43587                 return;
43588             }
43589         }
43590         this.activePanel = panel;
43591         panel.setActiveState(true);
43592         if(this.panelSize){
43593             panel.setSize(this.panelSize.width, this.panelSize.height);
43594         }
43595         if(this.closeBtn){
43596             this.closeBtn.setVisible(!this.config.closeOnTab && !this.isSlid && panel.isClosable());
43597         }
43598         this.updateTitle(panel.getTitle());
43599         if(this.tabs){
43600             this.fireEvent("invalidated", this);
43601         }
43602         this.fireEvent("panelactivated", this, panel);
43603     },
43604
43605     /**
43606      * Shows the specified panel.
43607      * @param {Number/String/ContentPanel} panelId The panel's index, id or the panel itself
43608      * @return {Roo.ContentPanel} The shown panel, or null if a panel could not be found from panelId
43609      */
43610     showPanel : function(panel)
43611     {
43612         panel = this.getPanel(panel);
43613         if(panel){
43614             if(this.tabs){
43615                 var tab = this.tabs.getTab(panel.getEl().id);
43616                 if(tab.isHidden()){
43617                     this.tabs.unhideTab(tab.id);
43618                 }
43619                 tab.activate();
43620             }else{
43621                 this.setActivePanel(panel);
43622             }
43623         }
43624         return panel;
43625     },
43626
43627     /**
43628      * Get the active panel for this region.
43629      * @return {Roo.ContentPanel} The active panel or null
43630      */
43631     getActivePanel : function(){
43632         return this.activePanel;
43633     },
43634
43635     validateVisibility : function(){
43636         if(this.panels.getCount() < 1){
43637             this.updateTitle("&#160;");
43638             this.closeBtn.hide();
43639             this.hide();
43640         }else{
43641             if(!this.isVisible()){
43642                 this.show();
43643             }
43644         }
43645     },
43646
43647     /**
43648      * Adds the passed ContentPanel(s) to this region.
43649      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
43650      * @return {Roo.ContentPanel} The panel added (if only one was added; null otherwise)
43651      */
43652     add : function(panel)
43653     {
43654         if(arguments.length > 1){
43655             for(var i = 0, len = arguments.length; i < len; i++) {
43656                 this.add(arguments[i]);
43657             }
43658             return null;
43659         }
43660         
43661         // if we have not been rendered yet, then we can not really do much of this..
43662         if (!this.bodyEl) {
43663             this.unrendered_panels.push(panel);
43664             return panel;
43665         }
43666         
43667         
43668         
43669         
43670         if(this.hasPanel(panel)){
43671             this.showPanel(panel);
43672             return panel;
43673         }
43674         panel.setRegion(this);
43675         this.panels.add(panel);
43676        /* if(this.panels.getCount() == 1 && !this.config.alwaysShowTabs){
43677             // sinle panel - no tab...?? would it not be better to render it with the tabs,
43678             // and hide them... ???
43679             this.bodyEl.dom.appendChild(panel.getEl().dom);
43680             if(panel.background !== true){
43681                 this.setActivePanel(panel);
43682             }
43683             this.fireEvent("paneladded", this, panel);
43684             return panel;
43685         }
43686         */
43687         if(!this.tabs){
43688             this.initTabs();
43689         }else{
43690             this.initPanelAsTab(panel);
43691         }
43692         
43693         
43694         if(panel.background !== true){
43695             this.tabs.activate(panel.getEl().id);
43696         }
43697         this.fireEvent("paneladded", this, panel);
43698         return panel;
43699     },
43700
43701     /**
43702      * Hides the tab for the specified panel.
43703      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
43704      */
43705     hidePanel : function(panel){
43706         if(this.tabs && (panel = this.getPanel(panel))){
43707             this.tabs.hideTab(panel.getEl().id);
43708         }
43709     },
43710
43711     /**
43712      * Unhides the tab for a previously hidden panel.
43713      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
43714      */
43715     unhidePanel : function(panel){
43716         if(this.tabs && (panel = this.getPanel(panel))){
43717             this.tabs.unhideTab(panel.getEl().id);
43718         }
43719     },
43720
43721     clearPanels : function(){
43722         while(this.panels.getCount() > 0){
43723              this.remove(this.panels.first());
43724         }
43725     },
43726
43727     /**
43728      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
43729      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
43730      * @param {Boolean} preservePanel Overrides the config preservePanel option
43731      * @return {Roo.ContentPanel} The panel that was removed
43732      */
43733     remove : function(panel, preservePanel)
43734     {
43735         panel = this.getPanel(panel);
43736         if(!panel){
43737             return null;
43738         }
43739         var e = {};
43740         this.fireEvent("beforeremove", this, panel, e);
43741         if(e.cancel === true){
43742             return null;
43743         }
43744         preservePanel = (typeof preservePanel != "undefined" ? preservePanel : (this.config.preservePanels === true || panel.preserve === true));
43745         var panelId = panel.getId();
43746         this.panels.removeKey(panelId);
43747         if(preservePanel){
43748             document.body.appendChild(panel.getEl().dom);
43749         }
43750         if(this.tabs){
43751             this.tabs.removeTab(panel.getEl().id);
43752         }else if (!preservePanel){
43753             this.bodyEl.dom.removeChild(panel.getEl().dom);
43754         }
43755         if(this.panels.getCount() == 1 && this.tabs && !this.config.alwaysShowTabs){
43756             var p = this.panels.first();
43757             var tempEl = document.createElement("div"); // temp holder to keep IE from deleting the node
43758             tempEl.appendChild(p.getEl().dom);
43759             this.bodyEl.update("");
43760             this.bodyEl.dom.appendChild(p.getEl().dom);
43761             tempEl = null;
43762             this.updateTitle(p.getTitle());
43763             this.tabs = null;
43764             this.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
43765             this.setActivePanel(p);
43766         }
43767         panel.setRegion(null);
43768         if(this.activePanel == panel){
43769             this.activePanel = null;
43770         }
43771         if(this.config.autoDestroy !== false && preservePanel !== true){
43772             try{panel.destroy();}catch(e){}
43773         }
43774         this.fireEvent("panelremoved", this, panel);
43775         return panel;
43776     },
43777
43778     /**
43779      * Returns the TabPanel component used by this region
43780      * @return {Roo.TabPanel}
43781      */
43782     getTabs : function(){
43783         return this.tabs;
43784     },
43785
43786     createTool : function(parentEl, className){
43787         var btn = Roo.DomHelper.append(parentEl, {
43788             tag: "div",
43789             cls: "x-layout-tools-button",
43790             children: [ {
43791                 tag: "div",
43792                 cls: "roo-layout-tools-button-inner " + className,
43793                 html: "&#160;"
43794             }]
43795         }, true);
43796         btn.addClassOnOver("roo-layout-tools-button-over");
43797         return btn;
43798     }
43799 });/*
43800  * Based on:
43801  * Ext JS Library 1.1.1
43802  * Copyright(c) 2006-2007, Ext JS, LLC.
43803  *
43804  * Originally Released Under LGPL - original licence link has changed is not relivant.
43805  *
43806  * Fork - LGPL
43807  * <script type="text/javascript">
43808  */
43809  
43810
43811
43812 /**
43813  * @class Roo.SplitLayoutRegion
43814  * @extends Roo.LayoutRegion
43815  * Adds a splitbar and other (private) useful functionality to a {@link Roo.LayoutRegion}.
43816  */
43817 Roo.bootstrap.layout.Split = function(config){
43818     this.cursor = config.cursor;
43819     Roo.bootstrap.layout.Split.superclass.constructor.call(this, config);
43820 };
43821
43822 Roo.extend(Roo.bootstrap.layout.Split, Roo.bootstrap.layout.Region,
43823 {
43824     splitTip : "Drag to resize.",
43825     collapsibleSplitTip : "Drag to resize. Double click to hide.",
43826     useSplitTips : false,
43827
43828     applyConfig : function(config){
43829         Roo.bootstrap.layout.Split.superclass.applyConfig.call(this, config);
43830     },
43831     
43832     onRender : function(ctr,pos) {
43833         
43834         Roo.bootstrap.layout.Split.superclass.onRender.call(this, ctr,pos);
43835         if(!this.config.split){
43836             return;
43837         }
43838         if(!this.split){
43839             
43840             var splitEl = Roo.DomHelper.append(ctr.dom,  {
43841                             tag: "div",
43842                             id: this.el.id + "-split",
43843                             cls: "roo-layout-split roo-layout-split-"+this.position,
43844                             html: "&#160;"
43845             });
43846             /** The SplitBar for this region 
43847             * @type Roo.SplitBar */
43848             // does not exist yet...
43849             Roo.log([this.position, this.orientation]);
43850             
43851             this.split = new Roo.bootstrap.SplitBar({
43852                 dragElement : splitEl,
43853                 resizingElement: this.el,
43854                 orientation : this.orientation
43855             });
43856             
43857             this.split.on("moved", this.onSplitMove, this);
43858             this.split.useShim = this.config.useShim === true;
43859             this.split.getMaximumSize = this[this.position == 'north' || this.position == 'south' ? 'getVMaxSize' : 'getHMaxSize'].createDelegate(this);
43860             if(this.useSplitTips){
43861                 this.split.el.dom.title = this.config.collapsible ? this.collapsibleSplitTip : this.splitTip;
43862             }
43863             //if(config.collapsible){
43864             //    this.split.el.on("dblclick", this.collapse,  this);
43865             //}
43866         }
43867         if(typeof this.config.minSize != "undefined"){
43868             this.split.minSize = this.config.minSize;
43869         }
43870         if(typeof this.config.maxSize != "undefined"){
43871             this.split.maxSize = this.config.maxSize;
43872         }
43873         if(this.config.hideWhenEmpty || this.config.hidden || this.config.collapsed){
43874             this.hideSplitter();
43875         }
43876         
43877     },
43878
43879     getHMaxSize : function(){
43880          var cmax = this.config.maxSize || 10000;
43881          var center = this.mgr.getRegion("center");
43882          return Math.min(cmax, (this.el.getWidth()+center.getEl().getWidth())-center.getMinWidth());
43883     },
43884
43885     getVMaxSize : function(){
43886          var cmax = this.config.maxSize || 10000;
43887          var center = this.mgr.getRegion("center");
43888          return Math.min(cmax, (this.el.getHeight()+center.getEl().getHeight())-center.getMinHeight());
43889     },
43890
43891     onSplitMove : function(split, newSize){
43892         this.fireEvent("resized", this, newSize);
43893     },
43894     
43895     /** 
43896      * Returns the {@link Roo.SplitBar} for this region.
43897      * @return {Roo.SplitBar}
43898      */
43899     getSplitBar : function(){
43900         return this.split;
43901     },
43902     
43903     hide : function(){
43904         this.hideSplitter();
43905         Roo.bootstrap.layout.Split.superclass.hide.call(this);
43906     },
43907
43908     hideSplitter : function(){
43909         if(this.split){
43910             this.split.el.setLocation(-2000,-2000);
43911             this.split.el.hide();
43912         }
43913     },
43914
43915     show : function(){
43916         if(this.split){
43917             this.split.el.show();
43918         }
43919         Roo.bootstrap.layout.Split.superclass.show.call(this);
43920     },
43921     
43922     beforeSlide: function(){
43923         if(Roo.isGecko){// firefox overflow auto bug workaround
43924             this.bodyEl.clip();
43925             if(this.tabs) {
43926                 this.tabs.bodyEl.clip();
43927             }
43928             if(this.activePanel){
43929                 this.activePanel.getEl().clip();
43930                 
43931                 if(this.activePanel.beforeSlide){
43932                     this.activePanel.beforeSlide();
43933                 }
43934             }
43935         }
43936     },
43937     
43938     afterSlide : function(){
43939         if(Roo.isGecko){// firefox overflow auto bug workaround
43940             this.bodyEl.unclip();
43941             if(this.tabs) {
43942                 this.tabs.bodyEl.unclip();
43943             }
43944             if(this.activePanel){
43945                 this.activePanel.getEl().unclip();
43946                 if(this.activePanel.afterSlide){
43947                     this.activePanel.afterSlide();
43948                 }
43949             }
43950         }
43951     },
43952
43953     initAutoHide : function(){
43954         if(this.autoHide !== false){
43955             if(!this.autoHideHd){
43956                 var st = new Roo.util.DelayedTask(this.slideIn, this);
43957                 this.autoHideHd = {
43958                     "mouseout": function(e){
43959                         if(!e.within(this.el, true)){
43960                             st.delay(500);
43961                         }
43962                     },
43963                     "mouseover" : function(e){
43964                         st.cancel();
43965                     },
43966                     scope : this
43967                 };
43968             }
43969             this.el.on(this.autoHideHd);
43970         }
43971     },
43972
43973     clearAutoHide : function(){
43974         if(this.autoHide !== false){
43975             this.el.un("mouseout", this.autoHideHd.mouseout);
43976             this.el.un("mouseover", this.autoHideHd.mouseover);
43977         }
43978     },
43979
43980     clearMonitor : function(){
43981         Roo.get(document).un("click", this.slideInIf, this);
43982     },
43983
43984     // these names are backwards but not changed for compat
43985     slideOut : function(){
43986         if(this.isSlid || this.el.hasActiveFx()){
43987             return;
43988         }
43989         this.isSlid = true;
43990         if(this.collapseBtn){
43991             this.collapseBtn.hide();
43992         }
43993         this.closeBtnState = this.closeBtn.getStyle('display');
43994         this.closeBtn.hide();
43995         if(this.stickBtn){
43996             this.stickBtn.show();
43997         }
43998         this.el.show();
43999         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor());
44000         this.beforeSlide();
44001         this.el.setStyle("z-index", 10001);
44002         this.el.slideIn(this.getSlideAnchor(), {
44003             callback: function(){
44004                 this.afterSlide();
44005                 this.initAutoHide();
44006                 Roo.get(document).on("click", this.slideInIf, this);
44007                 this.fireEvent("slideshow", this);
44008             },
44009             scope: this,
44010             block: true
44011         });
44012     },
44013
44014     afterSlideIn : function(){
44015         this.clearAutoHide();
44016         this.isSlid = false;
44017         this.clearMonitor();
44018         this.el.setStyle("z-index", "");
44019         if(this.collapseBtn){
44020             this.collapseBtn.show();
44021         }
44022         this.closeBtn.setStyle('display', this.closeBtnState);
44023         if(this.stickBtn){
44024             this.stickBtn.hide();
44025         }
44026         this.fireEvent("slidehide", this);
44027     },
44028
44029     slideIn : function(cb){
44030         if(!this.isSlid || this.el.hasActiveFx()){
44031             Roo.callback(cb);
44032             return;
44033         }
44034         this.isSlid = false;
44035         this.beforeSlide();
44036         this.el.slideOut(this.getSlideAnchor(), {
44037             callback: function(){
44038                 this.el.setLeftTop(-10000, -10000);
44039                 this.afterSlide();
44040                 this.afterSlideIn();
44041                 Roo.callback(cb);
44042             },
44043             scope: this,
44044             block: true
44045         });
44046     },
44047     
44048     slideInIf : function(e){
44049         if(!e.within(this.el)){
44050             this.slideIn();
44051         }
44052     },
44053
44054     animateCollapse : function(){
44055         this.beforeSlide();
44056         this.el.setStyle("z-index", 20000);
44057         var anchor = this.getSlideAnchor();
44058         this.el.slideOut(anchor, {
44059             callback : function(){
44060                 this.el.setStyle("z-index", "");
44061                 this.collapsedEl.slideIn(anchor, {duration:.3});
44062                 this.afterSlide();
44063                 this.el.setLocation(-10000,-10000);
44064                 this.el.hide();
44065                 this.fireEvent("collapsed", this);
44066             },
44067             scope: this,
44068             block: true
44069         });
44070     },
44071
44072     animateExpand : function(){
44073         this.beforeSlide();
44074         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor(), this.getExpandAdj());
44075         this.el.setStyle("z-index", 20000);
44076         this.collapsedEl.hide({
44077             duration:.1
44078         });
44079         this.el.slideIn(this.getSlideAnchor(), {
44080             callback : function(){
44081                 this.el.setStyle("z-index", "");
44082                 this.afterSlide();
44083                 if(this.split){
44084                     this.split.el.show();
44085                 }
44086                 this.fireEvent("invalidated", this);
44087                 this.fireEvent("expanded", this);
44088             },
44089             scope: this,
44090             block: true
44091         });
44092     },
44093
44094     anchors : {
44095         "west" : "left",
44096         "east" : "right",
44097         "north" : "top",
44098         "south" : "bottom"
44099     },
44100
44101     sanchors : {
44102         "west" : "l",
44103         "east" : "r",
44104         "north" : "t",
44105         "south" : "b"
44106     },
44107
44108     canchors : {
44109         "west" : "tl-tr",
44110         "east" : "tr-tl",
44111         "north" : "tl-bl",
44112         "south" : "bl-tl"
44113     },
44114
44115     getAnchor : function(){
44116         return this.anchors[this.position];
44117     },
44118
44119     getCollapseAnchor : function(){
44120         return this.canchors[this.position];
44121     },
44122
44123     getSlideAnchor : function(){
44124         return this.sanchors[this.position];
44125     },
44126
44127     getAlignAdj : function(){
44128         var cm = this.cmargins;
44129         switch(this.position){
44130             case "west":
44131                 return [0, 0];
44132             break;
44133             case "east":
44134                 return [0, 0];
44135             break;
44136             case "north":
44137                 return [0, 0];
44138             break;
44139             case "south":
44140                 return [0, 0];
44141             break;
44142         }
44143     },
44144
44145     getExpandAdj : function(){
44146         var c = this.collapsedEl, cm = this.cmargins;
44147         switch(this.position){
44148             case "west":
44149                 return [-(cm.right+c.getWidth()+cm.left), 0];
44150             break;
44151             case "east":
44152                 return [cm.right+c.getWidth()+cm.left, 0];
44153             break;
44154             case "north":
44155                 return [0, -(cm.top+cm.bottom+c.getHeight())];
44156             break;
44157             case "south":
44158                 return [0, cm.top+cm.bottom+c.getHeight()];
44159             break;
44160         }
44161     }
44162 });/*
44163  * Based on:
44164  * Ext JS Library 1.1.1
44165  * Copyright(c) 2006-2007, Ext JS, LLC.
44166  *
44167  * Originally Released Under LGPL - original licence link has changed is not relivant.
44168  *
44169  * Fork - LGPL
44170  * <script type="text/javascript">
44171  */
44172 /*
44173  * These classes are private internal classes
44174  */
44175 Roo.bootstrap.layout.Center = function(config){
44176     config.region = "center";
44177     Roo.bootstrap.layout.Region.call(this, config);
44178     this.visible = true;
44179     this.minWidth = config.minWidth || 20;
44180     this.minHeight = config.minHeight || 20;
44181 };
44182
44183 Roo.extend(Roo.bootstrap.layout.Center, Roo.bootstrap.layout.Region, {
44184     hide : function(){
44185         // center panel can't be hidden
44186     },
44187     
44188     show : function(){
44189         // center panel can't be hidden
44190     },
44191     
44192     getMinWidth: function(){
44193         return this.minWidth;
44194     },
44195     
44196     getMinHeight: function(){
44197         return this.minHeight;
44198     }
44199 });
44200
44201
44202
44203
44204  
44205
44206
44207
44208
44209
44210
44211 Roo.bootstrap.layout.North = function(config)
44212 {
44213     config.region = 'north';
44214     config.cursor = 'n-resize';
44215     
44216     Roo.bootstrap.layout.Split.call(this, config);
44217     
44218     
44219     if(this.split){
44220         this.split.placement = Roo.bootstrap.SplitBar.TOP;
44221         this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
44222         this.split.el.addClass("roo-layout-split-v");
44223     }
44224     //var size = config.initialSize || config.height;
44225     //if(this.el && typeof size != "undefined"){
44226     //    this.el.setHeight(size);
44227     //}
44228 };
44229 Roo.extend(Roo.bootstrap.layout.North, Roo.bootstrap.layout.Split,
44230 {
44231     orientation: Roo.bootstrap.SplitBar.VERTICAL,
44232      
44233      
44234     onRender : function(ctr, pos)
44235     {
44236         Roo.bootstrap.layout.Split.prototype.onRender.call(this, ctr, pos);
44237         var size = this.config.initialSize || this.config.height;
44238         if(this.el && typeof size != "undefined"){
44239             this.el.setHeight(size);
44240         }
44241     
44242     },
44243     
44244     getBox : function(){
44245         if(this.collapsed){
44246             return this.collapsedEl.getBox();
44247         }
44248         var box = this.el.getBox();
44249         if(this.split){
44250             box.height += this.split.el.getHeight();
44251         }
44252         return box;
44253     },
44254     
44255     updateBox : function(box){
44256         if(this.split && !this.collapsed){
44257             box.height -= this.split.el.getHeight();
44258             this.split.el.setLeft(box.x);
44259             this.split.el.setTop(box.y+box.height);
44260             this.split.el.setWidth(box.width);
44261         }
44262         if(this.collapsed){
44263             this.updateBody(box.width, null);
44264         }
44265         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
44266     }
44267 });
44268
44269
44270
44271
44272
44273 Roo.bootstrap.layout.South = function(config){
44274     config.region = 'south';
44275     config.cursor = 's-resize';
44276     Roo.bootstrap.layout.Split.call(this, config);
44277     if(this.split){
44278         this.split.placement = Roo.bootstrap.SplitBar.BOTTOM;
44279         this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
44280         this.split.el.addClass("roo-layout-split-v");
44281     }
44282     
44283 };
44284
44285 Roo.extend(Roo.bootstrap.layout.South, Roo.bootstrap.layout.Split, {
44286     orientation: Roo.bootstrap.SplitBar.VERTICAL,
44287     
44288     onRender : function(ctr, pos)
44289     {
44290         Roo.bootstrap.layout.Split.prototype.onRender.call(this, ctr, pos);
44291         var size = this.config.initialSize || this.config.height;
44292         if(this.el && typeof size != "undefined"){
44293             this.el.setHeight(size);
44294         }
44295     
44296     },
44297     
44298     getBox : function(){
44299         if(this.collapsed){
44300             return this.collapsedEl.getBox();
44301         }
44302         var box = this.el.getBox();
44303         if(this.split){
44304             var sh = this.split.el.getHeight();
44305             box.height += sh;
44306             box.y -= sh;
44307         }
44308         return box;
44309     },
44310     
44311     updateBox : function(box){
44312         if(this.split && !this.collapsed){
44313             var sh = this.split.el.getHeight();
44314             box.height -= sh;
44315             box.y += sh;
44316             this.split.el.setLeft(box.x);
44317             this.split.el.setTop(box.y-sh);
44318             this.split.el.setWidth(box.width);
44319         }
44320         if(this.collapsed){
44321             this.updateBody(box.width, null);
44322         }
44323         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
44324     }
44325 });
44326
44327 Roo.bootstrap.layout.East = function(config){
44328     config.region = "east";
44329     config.cursor = "e-resize";
44330     Roo.bootstrap.layout.Split.call(this, config);
44331     if(this.split){
44332         this.split.placement = Roo.bootstrap.SplitBar.RIGHT;
44333         this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
44334         this.split.el.addClass("roo-layout-split-h");
44335     }
44336     
44337 };
44338 Roo.extend(Roo.bootstrap.layout.East, Roo.bootstrap.layout.Split, {
44339     orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
44340     
44341     onRender : function(ctr, pos)
44342     {
44343         Roo.bootstrap.layout.Split.prototype.onRender.call(this, ctr, pos);
44344         var size = this.config.initialSize || this.config.width;
44345         if(this.el && typeof size != "undefined"){
44346             this.el.setWidth(size);
44347         }
44348     
44349     },
44350     
44351     getBox : function(){
44352         if(this.collapsed){
44353             return this.collapsedEl.getBox();
44354         }
44355         var box = this.el.getBox();
44356         if(this.split){
44357             var sw = this.split.el.getWidth();
44358             box.width += sw;
44359             box.x -= sw;
44360         }
44361         return box;
44362     },
44363
44364     updateBox : function(box){
44365         if(this.split && !this.collapsed){
44366             var sw = this.split.el.getWidth();
44367             box.width -= sw;
44368             this.split.el.setLeft(box.x);
44369             this.split.el.setTop(box.y);
44370             this.split.el.setHeight(box.height);
44371             box.x += sw;
44372         }
44373         if(this.collapsed){
44374             this.updateBody(null, box.height);
44375         }
44376         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
44377     }
44378 });
44379
44380 Roo.bootstrap.layout.West = function(config){
44381     config.region = "west";
44382     config.cursor = "w-resize";
44383     
44384     Roo.bootstrap.layout.Split.call(this, config);
44385     if(this.split){
44386         this.split.placement = Roo.bootstrap.SplitBar.LEFT;
44387         this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
44388         this.split.el.addClass("roo-layout-split-h");
44389     }
44390     
44391 };
44392 Roo.extend(Roo.bootstrap.layout.West, Roo.bootstrap.layout.Split, {
44393     orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
44394     
44395     onRender: function(ctr, pos)
44396     {
44397         Roo.bootstrap.layout.West.superclass.onRender.call(this, ctr,pos);
44398         var size = this.config.initialSize || this.config.width;
44399         if(typeof size != "undefined"){
44400             this.el.setWidth(size);
44401         }
44402     },
44403     
44404     getBox : function(){
44405         if(this.collapsed){
44406             return this.collapsedEl.getBox();
44407         }
44408         var box = this.el.getBox();
44409         if (box.width == 0) {
44410             box.width = this.config.width; // kludge?
44411         }
44412         if(this.split){
44413             box.width += this.split.el.getWidth();
44414         }
44415         return box;
44416     },
44417     
44418     updateBox : function(box){
44419         if(this.split && !this.collapsed){
44420             var sw = this.split.el.getWidth();
44421             box.width -= sw;
44422             this.split.el.setLeft(box.x+box.width);
44423             this.split.el.setTop(box.y);
44424             this.split.el.setHeight(box.height);
44425         }
44426         if(this.collapsed){
44427             this.updateBody(null, box.height);
44428         }
44429         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
44430     }
44431 });/*
44432  * Based on:
44433  * Ext JS Library 1.1.1
44434  * Copyright(c) 2006-2007, Ext JS, LLC.
44435  *
44436  * Originally Released Under LGPL - original licence link has changed is not relivant.
44437  *
44438  * Fork - LGPL
44439  * <script type="text/javascript">
44440  */
44441 /**
44442  * @class Roo.bootstrap.paenl.Content
44443  * @extends Roo.util.Observable
44444  * @children Roo.bootstrap.Component
44445  * @parent builder Roo.bootstrap.layout.Border
44446  * A basic ContentPanel element. - a panel that contain any content (eg. forms etc.)
44447  * @cfg {Boolean}   fitToFrame    True for this panel to adjust its size to fit when the region resizes  (defaults to false)
44448  * @cfg {Boolean}   fitContainer   When using {@link #fitToFrame} and {@link #resizeEl}, you can also fit the parent container  (defaults to false)
44449  * @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
44450  * @cfg {Boolean}   closable      True if the panel can be closed/removed
44451  * @cfg {Boolean}   background    True if the panel should not be activated when it is added (defaults to false)
44452  * @cfg {String/HTMLElement/Element} resizeEl An element to resize if {@link #fitToFrame} is true (instead of this panel's element)
44453  * @cfg {Toolbar}   toolbar       A toolbar for this panel
44454  * @cfg {Boolean} autoScroll    True to scroll overflow in this panel (use with {@link #fitToFrame})
44455  * @cfg {String} title          The title for this panel
44456  * @cfg {Array} adjustments     Values to <b>add</b> to the width/height when doing a {@link #fitToFrame} (default is [0, 0])
44457  * @cfg {String} url            Calls {@link #setUrl} with this value
44458  * @cfg {String} region  [required] (center|north|south|east|west) which region to put this panel on (when used with xtype constructors)
44459  * @cfg {String/Object} params  When used with {@link #url}, calls {@link #setUrl} with this value
44460  * @cfg {Boolean} loadOnce      When used with {@link #url}, calls {@link #setUrl} with this value
44461  * @cfg {String}    content        Raw content to fill content panel with (uses setContent on construction.)
44462  * @cfg {Boolean} iframe      contents are an iframe - makes showing remote sources/CSS feasible..
44463  * @cfg {Boolean} badges render the badges
44464  * @cfg {String} cls  extra classes to use  
44465  * @cfg {String} background (primary|secondary|success|info|warning|danger|light|dark)
44466  
44467  * @constructor
44468  * Create a new ContentPanel.
44469  * @param {String/Object} config A string to set only the title or a config object
44470  
44471  */
44472 Roo.bootstrap.panel.Content = function( config){
44473     
44474     this.tpl = config.tpl || false;
44475     
44476     var el = config.el;
44477     var content = config.content;
44478
44479     if(config.autoCreate){ // xtype is available if this is called from factory
44480         el = Roo.id();
44481     }
44482     this.el = Roo.get(el);
44483     if(!this.el && config && config.autoCreate){
44484         if(typeof config.autoCreate == "object"){
44485             if(!config.autoCreate.id){
44486                 config.autoCreate.id = config.id||el;
44487             }
44488             this.el = Roo.DomHelper.append(document.body,
44489                         config.autoCreate, true);
44490         }else{
44491             var elcfg =  {
44492                 tag: "div",
44493                 cls: (config.cls || '') +
44494                     (config.background ? ' bg-' + config.background : '') +
44495                     " roo-layout-inactive-content",
44496                 id: config.id||el
44497             };
44498             if (config.iframe) {
44499                 elcfg.cn = [
44500                     {
44501                         tag : 'iframe',
44502                         style : 'border: 0px',
44503                         src : 'about:blank'
44504                     }
44505                 ];
44506             }
44507               
44508             if (config.html) {
44509                 elcfg.html = config.html;
44510                 
44511             }
44512                         
44513             this.el = Roo.DomHelper.append(document.body, elcfg , true);
44514             if (config.iframe) {
44515                 this.iframeEl = this.el.select('iframe',true).first();
44516             }
44517             
44518         }
44519     } 
44520     this.closable = false;
44521     this.loaded = false;
44522     this.active = false;
44523    
44524       
44525     if (config.toolbar && !config.toolbar.el && config.toolbar.xtype) {
44526         
44527         this.toolbar = new config.toolbar.xns[config.toolbar.xtype](config.toolbar);
44528         
44529         this.wrapEl = this.el; //this.el.wrap();
44530         var ti = [];
44531         if (config.toolbar.items) {
44532             ti = config.toolbar.items ;
44533             delete config.toolbar.items ;
44534         }
44535         
44536         var nitems = [];
44537         this.toolbar.render(this.wrapEl, 'before');
44538         for(var i =0;i < ti.length;i++) {
44539           //  Roo.log(['add child', items[i]]);
44540             nitems.push(this.toolbar.addxtype(Roo.apply({}, ti[i])));
44541         }
44542         this.toolbar.items = nitems;
44543         this.toolbar.el.insertBefore(this.wrapEl.dom.firstChild);
44544         delete config.toolbar;
44545         
44546     }
44547     /*
44548     // xtype created footer. - not sure if will work as we normally have to render first..
44549     if (this.footer && !this.footer.el && this.footer.xtype) {
44550         if (!this.wrapEl) {
44551             this.wrapEl = this.el.wrap();
44552         }
44553     
44554         this.footer.container = this.wrapEl.createChild();
44555          
44556         this.footer = Roo.factory(this.footer, Roo);
44557         
44558     }
44559     */
44560     
44561      if(typeof config == "string"){
44562         this.title = config;
44563     }else{
44564         Roo.apply(this, config);
44565     }
44566     
44567     if(this.resizeEl){
44568         this.resizeEl = Roo.get(this.resizeEl, true);
44569     }else{
44570         this.resizeEl = this.el;
44571     }
44572     // handle view.xtype
44573     
44574  
44575     
44576     
44577     this.addEvents({
44578         /**
44579          * @event activate
44580          * Fires when this panel is activated. 
44581          * @param {Roo.ContentPanel} this
44582          */
44583         "activate" : true,
44584         /**
44585          * @event deactivate
44586          * Fires when this panel is activated. 
44587          * @param {Roo.ContentPanel} this
44588          */
44589         "deactivate" : true,
44590
44591         /**
44592          * @event resize
44593          * Fires when this panel is resized if fitToFrame is true.
44594          * @param {Roo.ContentPanel} this
44595          * @param {Number} width The width after any component adjustments
44596          * @param {Number} height The height after any component adjustments
44597          */
44598         "resize" : true,
44599         
44600          /**
44601          * @event render
44602          * Fires when this tab is created
44603          * @param {Roo.ContentPanel} this
44604          */
44605         "render" : true,
44606         
44607           /**
44608          * @event scroll
44609          * Fires when this content is scrolled
44610          * @param {Roo.ContentPanel} this
44611          * @param {Event} scrollEvent
44612          */
44613         "scroll" : true
44614         
44615         
44616         
44617     });
44618     
44619
44620     
44621     
44622     if(this.autoScroll && !this.iframe){
44623         this.resizeEl.setStyle("overflow", "auto");
44624         this.resizeEl.on('scroll', this.onScroll, this);
44625     } else {
44626         // fix randome scrolling
44627         //this.el.on('scroll', function() {
44628         //    Roo.log('fix random scolling');
44629         //    this.scrollTo('top',0); 
44630         //});
44631     }
44632     content = content || this.content;
44633     if(content){
44634         this.setContent(content);
44635     }
44636     if(config && config.url){
44637         this.setUrl(this.url, this.params, this.loadOnce);
44638     }
44639     
44640     
44641     
44642     Roo.bootstrap.panel.Content.superclass.constructor.call(this);
44643     
44644     if (this.view && typeof(this.view.xtype) != 'undefined') {
44645         this.view.el = this.el.appendChild(document.createElement("div"));
44646         this.view = Roo.factory(this.view); 
44647         this.view.render  &&  this.view.render(false, '');  
44648     }
44649     
44650     
44651     this.fireEvent('render', this);
44652 };
44653
44654 Roo.extend(Roo.bootstrap.panel.Content, Roo.bootstrap.Component, {
44655     
44656     cls : '',
44657     background : '',
44658     
44659     tabTip : '',
44660     
44661     iframe : false,
44662     iframeEl : false,
44663     
44664     /* Resize Element - use this to work out scroll etc. */
44665     resizeEl : false,
44666     
44667     setRegion : function(region){
44668         this.region = region;
44669         this.setActiveClass(region && !this.background);
44670     },
44671     
44672     
44673     setActiveClass: function(state)
44674     {
44675         if(state){
44676            this.el.replaceClass("roo-layout-inactive-content", "roo-layout-active-content");
44677            this.el.setStyle('position','relative');
44678         }else{
44679            this.el.replaceClass("roo-layout-active-content", "roo-layout-inactive-content");
44680            this.el.setStyle('position', 'absolute');
44681         } 
44682     },
44683     
44684     /**
44685      * Returns the toolbar for this Panel if one was configured. 
44686      * @return {Roo.Toolbar} 
44687      */
44688     getToolbar : function(){
44689         return this.toolbar;
44690     },
44691     
44692     setActiveState : function(active)
44693     {
44694         this.active = active;
44695         this.setActiveClass(active);
44696         if(!active){
44697             if(this.fireEvent("deactivate", this) === false){
44698                 return false;
44699             }
44700             return true;
44701         }
44702         this.fireEvent("activate", this);
44703         return true;
44704     },
44705     /**
44706      * Updates this panel's element (not for iframe)
44707      * @param {String} content The new content
44708      * @param {Boolean} loadScripts (optional) true to look for and process scripts
44709     */
44710     setContent : function(content, loadScripts){
44711         if (this.iframe) {
44712             return;
44713         }
44714         
44715         this.el.update(content, loadScripts);
44716     },
44717
44718     ignoreResize : function(w, h)
44719     {
44720         //return false; // always resize?
44721         if(this.lastSize && this.lastSize.width == w && this.lastSize.height == h){
44722             return true;
44723         }else{
44724             this.lastSize = {width: w, height: h};
44725             return false;
44726         }
44727     },
44728     /**
44729      * Get the {@link Roo.UpdateManager} for this panel. Enables you to perform Ajax updates.
44730      * @return {Roo.UpdateManager} The UpdateManager
44731      */
44732     getUpdateManager : function(){
44733         if (this.iframe) {
44734             return false;
44735         }
44736         return this.el.getUpdateManager();
44737     },
44738      /**
44739      * Loads this content panel immediately with content from XHR. Note: to delay loading until the panel is activated, use {@link #setUrl}.
44740      * Does not work with IFRAME contents
44741      * @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:
44742 <pre><code>
44743 panel.load({
44744     url: "your-url.php",
44745     params: {param1: "foo", param2: "bar"}, // or a URL encoded string
44746     callback: yourFunction,
44747     scope: yourObject, //(optional scope)
44748     discardUrl: false,
44749     nocache: false,
44750     text: "Loading...",
44751     timeout: 30,
44752     scripts: false
44753 });
44754 </code></pre>
44755      
44756      * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
44757      * 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.
44758      * @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}
44759      * @param {Function} callback (optional) Callback when transaction is complete -- called with signature (oElement, bSuccess, oResponse)
44760      * @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.
44761      * @return {Roo.ContentPanel} this
44762      */
44763     load : function(){
44764         
44765         if (this.iframe) {
44766             return this;
44767         }
44768         
44769         var um = this.el.getUpdateManager();
44770         um.update.apply(um, arguments);
44771         return this;
44772     },
44773
44774
44775     /**
44776      * 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.
44777      * @param {String/Function} url The URL to load the content from or a function to call to get the URL
44778      * @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)
44779      * @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)
44780      * @return {Roo.UpdateManager|Boolean} The UpdateManager or false if IFRAME
44781      */
44782     setUrl : function(url, params, loadOnce){
44783         if (this.iframe) {
44784             this.iframeEl.dom.src = url;
44785             return false;
44786         }
44787         
44788         if(this.refreshDelegate){
44789             this.removeListener("activate", this.refreshDelegate);
44790         }
44791         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
44792         this.on("activate", this.refreshDelegate);
44793         return this.el.getUpdateManager();
44794     },
44795     
44796     _handleRefresh : function(url, params, loadOnce){
44797         if(!loadOnce || !this.loaded){
44798             var updater = this.el.getUpdateManager();
44799             updater.update(url, params, this._setLoaded.createDelegate(this));
44800         }
44801     },
44802     
44803     _setLoaded : function(){
44804         this.loaded = true;
44805     }, 
44806     
44807     /**
44808      * Returns this panel's id
44809      * @return {String} 
44810      */
44811     getId : function(){
44812         return this.el.id;
44813     },
44814     
44815     /** 
44816      * Returns this panel's element - used by regiosn to add.
44817      * @return {Roo.Element} 
44818      */
44819     getEl : function(){
44820         return this.wrapEl || this.el;
44821     },
44822     
44823    
44824     
44825     adjustForComponents : function(width, height)
44826     {
44827         //Roo.log('adjustForComponents ');
44828         if(this.resizeEl != this.el){
44829             width -= this.el.getFrameWidth('lr');
44830             height -= this.el.getFrameWidth('tb');
44831         }
44832         if(this.toolbar){
44833             var te = this.toolbar.getEl();
44834             te.setWidth(width);
44835             height -= te.getHeight();
44836         }
44837         if(this.footer){
44838             var te = this.footer.getEl();
44839             te.setWidth(width);
44840             height -= te.getHeight();
44841         }
44842         
44843         
44844         if(this.adjustments){
44845             width += this.adjustments[0];
44846             height += this.adjustments[1];
44847         }
44848         return {"width": width, "height": height};
44849     },
44850     
44851     setSize : function(width, height){
44852         if(this.fitToFrame && !this.ignoreResize(width, height)){
44853             if(this.fitContainer && this.resizeEl != this.el){
44854                 this.el.setSize(width, height);
44855             }
44856             var size = this.adjustForComponents(width, height);
44857             if (this.iframe) {
44858                 this.iframeEl.setSize(width,height);
44859             }
44860             
44861             this.resizeEl.setSize(this.autoWidth ? "auto" : size.width, this.autoHeight ? "auto" : size.height);
44862             this.fireEvent('resize', this, size.width, size.height);
44863             
44864             
44865         }
44866     },
44867     
44868     /**
44869      * Returns this panel's title
44870      * @return {String} 
44871      */
44872     getTitle : function(){
44873         
44874         if (typeof(this.title) != 'object') {
44875             return this.title;
44876         }
44877         
44878         var t = '';
44879         for (var k in this.title) {
44880             if (!this.title.hasOwnProperty(k)) {
44881                 continue;
44882             }
44883             
44884             if (k.indexOf('-') >= 0) {
44885                 var s = k.split('-');
44886                 for (var i = 0; i<s.length; i++) {
44887                     t += "<span class='visible-"+s[i]+"'>"+this.title[k]+"</span>";
44888                 }
44889             } else {
44890                 t += "<span class='visible-"+k+"'>"+this.title[k]+"</span>";
44891             }
44892         }
44893         return t;
44894     },
44895     
44896     /**
44897      * Set this panel's title
44898      * @param {String} title
44899      */
44900     setTitle : function(title){
44901         this.title = title;
44902         if(this.region){
44903             this.region.updatePanelTitle(this, title);
44904         }
44905     },
44906     
44907     /**
44908      * Returns true is this panel was configured to be closable
44909      * @return {Boolean} 
44910      */
44911     isClosable : function(){
44912         return this.closable;
44913     },
44914     
44915     beforeSlide : function(){
44916         this.el.clip();
44917         this.resizeEl.clip();
44918     },
44919     
44920     afterSlide : function(){
44921         this.el.unclip();
44922         this.resizeEl.unclip();
44923     },
44924     
44925     /**
44926      *   Force a content refresh from the URL specified in the {@link #setUrl} method.
44927      *   Will fail silently if the {@link #setUrl} method has not been called.
44928      *   This does not activate the panel, just updates its content.
44929      */
44930     refresh : function(){
44931         if(this.refreshDelegate){
44932            this.loaded = false;
44933            this.refreshDelegate();
44934         }
44935     },
44936     
44937     /**
44938      * Destroys this panel
44939      */
44940     destroy : function(){
44941         this.el.removeAllListeners();
44942         var tempEl = document.createElement("span");
44943         tempEl.appendChild(this.el.dom);
44944         tempEl.innerHTML = "";
44945         this.el.remove();
44946         this.el = null;
44947     },
44948     
44949     /**
44950      * form - if the content panel contains a form - this is a reference to it.
44951      * @type {Roo.form.Form}
44952      */
44953     form : false,
44954     /**
44955      * view - if the content panel contains a view (Roo.DatePicker / Roo.View / Roo.JsonView)
44956      *    This contains a reference to it.
44957      * @type {Roo.View}
44958      */
44959     view : false,
44960     
44961       /**
44962      * Adds a xtype elements to the panel - currently only supports Forms, View, JsonView.
44963      * <pre><code>
44964
44965 layout.addxtype({
44966        xtype : 'Form',
44967        items: [ .... ]
44968    }
44969 );
44970
44971 </code></pre>
44972      * @param {Object} cfg Xtype definition of item to add.
44973      */
44974     
44975     
44976     getChildContainer: function () {
44977         return this.getEl();
44978     },
44979     
44980     
44981     onScroll : function(e)
44982     {
44983         this.fireEvent('scroll', this, e);
44984     }
44985     
44986     
44987     /*
44988         var  ret = new Roo.factory(cfg);
44989         return ret;
44990         
44991         
44992         // add form..
44993         if (cfg.xtype.match(/^Form$/)) {
44994             
44995             var el;
44996             //if (this.footer) {
44997             //    el = this.footer.container.insertSibling(false, 'before');
44998             //} else {
44999                 el = this.el.createChild();
45000             //}
45001
45002             this.form = new  Roo.form.Form(cfg);
45003             
45004             
45005             if ( this.form.allItems.length) {
45006                 this.form.render(el.dom);
45007             }
45008             return this.form;
45009         }
45010         // should only have one of theses..
45011         if ([ 'View', 'JsonView', 'DatePicker'].indexOf(cfg.xtype) > -1) {
45012             // views.. should not be just added - used named prop 'view''
45013             
45014             cfg.el = this.el.appendChild(document.createElement("div"));
45015             // factory?
45016             
45017             var ret = new Roo.factory(cfg);
45018              
45019              ret.render && ret.render(false, ''); // render blank..
45020             this.view = ret;
45021             return ret;
45022         }
45023         return false;
45024     }
45025     \*/
45026 });
45027  
45028 /**
45029  * @class Roo.bootstrap.panel.Grid
45030  * @extends Roo.bootstrap.panel.Content
45031  * @constructor
45032  * Create a new GridPanel.
45033  * @cfg {Roo.bootstrap.Table} grid The grid for this panel
45034  * @cfg {Roo.bootstrap.nav.Simplebar} toolbar the toolbar at the top of the grid.
45035  * @param {Object} config A the config object
45036   
45037  */
45038
45039
45040
45041 Roo.bootstrap.panel.Grid = function(config)
45042 {
45043     
45044       
45045     this.wrapper = Roo.DomHelper.append(document.body, // wrapper for IE7 strict & safari scroll issue
45046         {tag: "div", cls: "roo-layout-grid-wrapper roo-layout-inactive-content"}, true);
45047
45048     config.el = this.wrapper;
45049     //this.el = this.wrapper;
45050     
45051       if (config.container) {
45052         // ctor'ed from a Border/panel.grid
45053         
45054         
45055         this.wrapper.setStyle("overflow", "hidden");
45056         this.wrapper.addClass('roo-grid-container');
45057
45058     }
45059     
45060     
45061     if(config.toolbar){
45062         var tool_el = this.wrapper.createChild();    
45063         this.toolbar = Roo.factory(config.toolbar);
45064         var ti = [];
45065         if (config.toolbar.items) {
45066             ti = config.toolbar.items ;
45067             delete config.toolbar.items ;
45068         }
45069         
45070         var nitems = [];
45071         this.toolbar.render(tool_el);
45072         for(var i =0;i < ti.length;i++) {
45073           //  Roo.log(['add child', items[i]]);
45074             nitems.push(this.toolbar.addxtype(Roo.apply({}, ti[i])));
45075         }
45076         this.toolbar.items = nitems;
45077         
45078         delete config.toolbar;
45079     }
45080     
45081     Roo.bootstrap.panel.Grid.superclass.constructor.call(this, config);
45082     config.grid.scrollBody = true;;
45083     config.grid.monitorWindowResize = false; // turn off autosizing
45084     config.grid.autoHeight = false;
45085     config.grid.autoWidth = false;
45086     
45087     this.grid = new config.grid.xns[config.grid.xtype](config.grid);
45088     
45089     if (config.background) {
45090         // render grid on panel activation (if panel background)
45091         this.on('activate', function(gp) {
45092             if (!gp.grid.rendered) {
45093                 gp.grid.render(this.wrapper);
45094                 gp.grid.getGridEl().replaceClass("roo-layout-inactive-content", "roo-layout-component-panel");   
45095             }
45096         });
45097             
45098     } else {
45099         this.grid.render(this.wrapper);
45100         this.grid.getGridEl().replaceClass("roo-layout-inactive-content", "roo-layout-component-panel");               
45101
45102     }
45103     //this.wrapper.dom.appendChild(config.grid.getGridEl().dom);
45104     // ??? needed ??? config.el = this.wrapper;
45105     
45106     
45107     
45108   
45109     // xtype created footer. - not sure if will work as we normally have to render first..
45110     if (this.footer && !this.footer.el && this.footer.xtype) {
45111         
45112         var ctr = this.grid.getView().getFooterPanel(true);
45113         this.footer.dataSource = this.grid.dataSource;
45114         this.footer = Roo.factory(this.footer, Roo);
45115         this.footer.render(ctr);
45116         
45117     }
45118     
45119     
45120     
45121     
45122      
45123 };
45124
45125 Roo.extend(Roo.bootstrap.panel.Grid, Roo.bootstrap.panel.Content,
45126 {
45127   
45128     getId : function(){
45129         return this.grid.id;
45130     },
45131     
45132     /**
45133      * Returns the grid for this panel
45134      * @return {Roo.bootstrap.Table} 
45135      */
45136     getGrid : function(){
45137         return this.grid;    
45138     },
45139     
45140     setSize : function(width, height)
45141     {
45142      
45143         //if(!this.ignoreResize(width, height)){
45144             var grid = this.grid;
45145             var size = this.adjustForComponents(width, height);
45146             // tfoot is not a footer?
45147           
45148             
45149             var gridel = grid.getGridEl();
45150             gridel.setSize(size.width, size.height);
45151             
45152             var tbd = grid.getGridEl().select('tbody', true).first();
45153             var thd = grid.getGridEl().select('thead',true).first();
45154             var tbf= grid.getGridEl().select('tfoot', true).first();
45155
45156             if (tbf) {
45157                 size.height -= tbf.getHeight();
45158             }
45159             if (thd) {
45160                 size.height -= thd.getHeight();
45161             }
45162             
45163             tbd.setSize(size.width, size.height );
45164             // this is for the account management tab -seems to work there.
45165             var thd = grid.getGridEl().select('thead',true).first();
45166             //if (tbd) {
45167             //    tbd.setSize(size.width, size.height - thd.getHeight());
45168             //}
45169              
45170             grid.autoSize();
45171         //}
45172    
45173     },
45174      
45175     
45176     
45177     beforeSlide : function(){
45178         this.grid.getView().scroller.clip();
45179     },
45180     
45181     afterSlide : function(){
45182         this.grid.getView().scroller.unclip();
45183     },
45184     
45185     destroy : function(){
45186         this.grid.destroy();
45187         delete this.grid;
45188         Roo.bootstrap.panel.Grid.superclass.destroy.call(this); 
45189     }
45190 });
45191
45192 /**
45193  * @class Roo.bootstrap.panel.Nest
45194  * @extends Roo.bootstrap.panel.Content
45195  * @constructor
45196  * Create a new Panel, that can contain a layout.Border.
45197  * 
45198  * 
45199  * @param {String/Object} config A string to set only the title or a config object
45200  */
45201 Roo.bootstrap.panel.Nest = function(config)
45202 {
45203     // construct with only one argument..
45204     /* FIXME - implement nicer consturctors
45205     if (layout.layout) {
45206         config = layout;
45207         layout = config.layout;
45208         delete config.layout;
45209     }
45210     if (layout.xtype && !layout.getEl) {
45211         // then layout needs constructing..
45212         layout = Roo.factory(layout, Roo);
45213     }
45214     */
45215     
45216     config.el =  config.layout.getEl();
45217     
45218     Roo.bootstrap.panel.Nest.superclass.constructor.call(this, config);
45219     
45220     config.layout.monitorWindowResize = false; // turn off autosizing
45221     this.layout = config.layout;
45222     this.layout.getEl().addClass("roo-layout-nested-layout");
45223     this.layout.parent = this;
45224     
45225     
45226     
45227     
45228 };
45229
45230 Roo.extend(Roo.bootstrap.panel.Nest, Roo.bootstrap.panel.Content, {
45231     /**
45232     * @cfg {Roo.BorderLayout} layout The layout for this panel
45233     */
45234     layout : false,
45235
45236     setSize : function(width, height){
45237         if(!this.ignoreResize(width, height)){
45238             var size = this.adjustForComponents(width, height);
45239             var el = this.layout.getEl();
45240             if (size.height < 1) {
45241                 el.setWidth(size.width);   
45242             } else {
45243                 el.setSize(size.width, size.height);
45244             }
45245             var touch = el.dom.offsetWidth;
45246             this.layout.layout();
45247             // ie requires a double layout on the first pass
45248             if(Roo.isIE && !this.initialized){
45249                 this.initialized = true;
45250                 this.layout.layout();
45251             }
45252         }
45253     },
45254     
45255     // activate all subpanels if not currently active..
45256     
45257     setActiveState : function(active){
45258         this.active = active;
45259         this.setActiveClass(active);
45260         
45261         if(!active){
45262             this.fireEvent("deactivate", this);
45263             return;
45264         }
45265         
45266         this.fireEvent("activate", this);
45267         // not sure if this should happen before or after..
45268         if (!this.layout) {
45269             return; // should not happen..
45270         }
45271         var reg = false;
45272         for (var r in this.layout.regions) {
45273             reg = this.layout.getRegion(r);
45274             if (reg.getActivePanel()) {
45275                 //reg.showPanel(reg.getActivePanel()); // force it to activate.. 
45276                 reg.setActivePanel(reg.getActivePanel());
45277                 continue;
45278             }
45279             if (!reg.panels.length) {
45280                 continue;
45281             }
45282             reg.showPanel(reg.getPanel(0));
45283         }
45284         
45285         
45286         
45287         
45288     },
45289     
45290     /**
45291      * Returns the nested BorderLayout for this panel
45292      * @return {Roo.BorderLayout} 
45293      */
45294     getLayout : function(){
45295         return this.layout;
45296     },
45297     
45298      /**
45299      * Adds a xtype elements to the layout of the nested panel
45300      * <pre><code>
45301
45302 panel.addxtype({
45303        xtype : 'ContentPanel',
45304        region: 'west',
45305        items: [ .... ]
45306    }
45307 );
45308
45309 panel.addxtype({
45310         xtype : 'NestedLayoutPanel',
45311         region: 'west',
45312         layout: {
45313            center: { },
45314            west: { }   
45315         },
45316         items : [ ... list of content panels or nested layout panels.. ]
45317    }
45318 );
45319 </code></pre>
45320      * @param {Object} cfg Xtype definition of item to add.
45321      */
45322     addxtype : function(cfg) {
45323         return this.layout.addxtype(cfg);
45324     
45325     }
45326 });/*
45327  * Based on:
45328  * Ext JS Library 1.1.1
45329  * Copyright(c) 2006-2007, Ext JS, LLC.
45330  *
45331  * Originally Released Under LGPL - original licence link has changed is not relivant.
45332  *
45333  * Fork - LGPL
45334  * <script type="text/javascript">
45335  */
45336 /**
45337  * @class Roo.TabPanel
45338  * @extends Roo.util.Observable
45339  * A lightweight tab container.
45340  * <br><br>
45341  * Usage:
45342  * <pre><code>
45343 // basic tabs 1, built from existing content
45344 var tabs = new Roo.TabPanel("tabs1");
45345 tabs.addTab("script", "View Script");
45346 tabs.addTab("markup", "View Markup");
45347 tabs.activate("script");
45348
45349 // more advanced tabs, built from javascript
45350 var jtabs = new Roo.TabPanel("jtabs");
45351 jtabs.addTab("jtabs-1", "Normal Tab", "My content was added during construction.");
45352
45353 // set up the UpdateManager
45354 var tab2 = jtabs.addTab("jtabs-2", "Ajax Tab 1");
45355 var updater = tab2.getUpdateManager();
45356 updater.setDefaultUrl("ajax1.htm");
45357 tab2.on('activate', updater.refresh, updater, true);
45358
45359 // Use setUrl for Ajax loading
45360 var tab3 = jtabs.addTab("jtabs-3", "Ajax Tab 2");
45361 tab3.setUrl("ajax2.htm", null, true);
45362
45363 // Disabled tab
45364 var tab4 = jtabs.addTab("tabs1-5", "Disabled Tab", "Can't see me cause I'm disabled");
45365 tab4.disable();
45366
45367 jtabs.activate("jtabs-1");
45368  * </code></pre>
45369  * @constructor
45370  * Create a new TabPanel.
45371  * @param {String/HTMLElement/Roo.Element} container The id, DOM element or Roo.Element container where this TabPanel is to be rendered.
45372  * @param {Object/Boolean} config Config object to set any properties for this TabPanel, or true to render the tabs on the bottom.
45373  */
45374 Roo.bootstrap.panel.Tabs = function(config){
45375     /**
45376     * The container element for this TabPanel.
45377     * @type Roo.Element
45378     */
45379     this.el = Roo.get(config.el);
45380     delete config.el;
45381     if(config){
45382         if(typeof config == "boolean"){
45383             this.tabPosition = config ? "bottom" : "top";
45384         }else{
45385             Roo.apply(this, config);
45386         }
45387     }
45388     
45389     if(this.tabPosition == "bottom"){
45390         // if tabs are at the bottom = create the body first.
45391         this.bodyEl = Roo.get(this.createBody(this.el.dom));
45392         this.el.addClass("roo-tabs-bottom");
45393     }
45394     // next create the tabs holders
45395     
45396     if (this.tabPosition == "west"){
45397         
45398         var reg = this.region; // fake it..
45399         while (reg) {
45400             if (!reg.mgr.parent) {
45401                 break;
45402             }
45403             reg = reg.mgr.parent.region;
45404         }
45405         Roo.log("got nest?");
45406         Roo.log(reg);
45407         if (reg.mgr.getRegion('west')) {
45408             var ctrdom = reg.mgr.getRegion('west').bodyEl.dom;
45409             this.stripWrap = Roo.get(this.createStrip(ctrdom ), true);
45410             this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
45411             this.stripEl.setVisibilityMode(Roo.Element.DISPLAY);
45412             this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
45413         
45414             
45415         }
45416         
45417         
45418     } else {
45419      
45420         this.stripWrap = Roo.get(this.createStrip(this.el.dom), true);
45421         this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
45422         this.stripEl.setVisibilityMode(Roo.Element.DISPLAY);
45423         this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
45424     }
45425     
45426     
45427     if(Roo.isIE){
45428         Roo.fly(this.stripWrap.dom.firstChild).setStyle("overflow-x", "hidden");
45429     }
45430     
45431     // finally - if tabs are at the top, then create the body last..
45432     if(this.tabPosition != "bottom"){
45433         /** The body element that contains {@link Roo.TabPanelItem} bodies. +
45434          * @type Roo.Element
45435          */
45436         this.bodyEl = Roo.get(this.createBody(this.el.dom));
45437         this.el.addClass("roo-tabs-top");
45438     }
45439     this.items = [];
45440
45441     this.bodyEl.setStyle("position", "relative");
45442
45443     this.active = null;
45444     this.activateDelegate = this.activate.createDelegate(this);
45445
45446     this.addEvents({
45447         /**
45448          * @event tabchange
45449          * Fires when the active tab changes
45450          * @param {Roo.TabPanel} this
45451          * @param {Roo.TabPanelItem} activePanel The new active tab
45452          */
45453         "tabchange": true,
45454         /**
45455          * @event beforetabchange
45456          * Fires before the active tab changes, set cancel to true on the "e" parameter to cancel the change
45457          * @param {Roo.TabPanel} this
45458          * @param {Object} e Set cancel to true on this object to cancel the tab change
45459          * @param {Roo.TabPanelItem} tab The tab being changed to
45460          */
45461         "beforetabchange" : true
45462     });
45463
45464     Roo.EventManager.onWindowResize(this.onResize, this);
45465     this.cpad = this.el.getPadding("lr");
45466     this.hiddenCount = 0;
45467
45468
45469     // toolbar on the tabbar support...
45470     if (this.toolbar) {
45471         alert("no toolbar support yet");
45472         this.toolbar  = false;
45473         /*
45474         var tcfg = this.toolbar;
45475         tcfg.container = this.stripEl.child('td.x-tab-strip-toolbar');  
45476         this.toolbar = new Roo.Toolbar(tcfg);
45477         if (Roo.isSafari) {
45478             var tbl = tcfg.container.child('table', true);
45479             tbl.setAttribute('width', '100%');
45480         }
45481         */
45482         
45483     }
45484    
45485
45486
45487     Roo.bootstrap.panel.Tabs.superclass.constructor.call(this);
45488 };
45489
45490 Roo.extend(Roo.bootstrap.panel.Tabs, Roo.util.Observable, {
45491     /*
45492      *@cfg {String} tabPosition "top" or "bottom" (defaults to "top")
45493      */
45494     tabPosition : "top",
45495     /*
45496      *@cfg {Number} currentTabWidth The width of the current tab (defaults to 0)
45497      */
45498     currentTabWidth : 0,
45499     /*
45500      *@cfg {Number} minTabWidth The minimum width of a tab (defaults to 40) (ignored if {@link #resizeTabs} is not true)
45501      */
45502     minTabWidth : 40,
45503     /*
45504      *@cfg {Number} maxTabWidth The maximum width of a tab (defaults to 250) (ignored if {@link #resizeTabs} is not true)
45505      */
45506     maxTabWidth : 250,
45507     /*
45508      *@cfg {Number} preferredTabWidth The preferred (default) width of a tab (defaults to 175) (ignored if {@link #resizeTabs} is not true)
45509      */
45510     preferredTabWidth : 175,
45511     /*
45512      *@cfg {Boolean} resizeTabs True to enable dynamic tab resizing (defaults to false)
45513      */
45514     resizeTabs : false,
45515     /*
45516      *@cfg {Boolean} monitorResize Set this to true to turn on window resize monitoring (ignored if {@link #resizeTabs} is not true) (defaults to true)
45517      */
45518     monitorResize : true,
45519     /*
45520      *@cfg {Object} toolbar xtype description of toolbar to show at the right of the tab bar. 
45521      */
45522     toolbar : false,  // set by caller..
45523     
45524     region : false, /// set by caller
45525     
45526     disableTooltips : true, // not used yet...
45527
45528     /**
45529      * Creates a new {@link Roo.TabPanelItem} by looking for an existing element with the provided id -- if it's not found it creates one.
45530      * @param {String} id The id of the div to use <b>or create</b>
45531      * @param {String} text The text for the tab
45532      * @param {String} content (optional) Content to put in the TabPanelItem body
45533      * @param {Boolean} closable (optional) True to create a close icon on the tab
45534      * @return {Roo.TabPanelItem} The created TabPanelItem
45535      */
45536     addTab : function(id, text, content, closable, tpl)
45537     {
45538         var item = new Roo.bootstrap.panel.TabItem({
45539             panel: this,
45540             id : id,
45541             text : text,
45542             closable : closable,
45543             tpl : tpl
45544         });
45545         this.addTabItem(item);
45546         if(content){
45547             item.setContent(content);
45548         }
45549         return item;
45550     },
45551
45552     /**
45553      * Returns the {@link Roo.TabPanelItem} with the specified id/index
45554      * @param {String/Number} id The id or index of the TabPanelItem to fetch.
45555      * @return {Roo.TabPanelItem}
45556      */
45557     getTab : function(id){
45558         return this.items[id];
45559     },
45560
45561     /**
45562      * Hides the {@link Roo.TabPanelItem} with the specified id/index
45563      * @param {String/Number} id The id or index of the TabPanelItem to hide.
45564      */
45565     hideTab : function(id){
45566         var t = this.items[id];
45567         if(!t.isHidden()){
45568            t.setHidden(true);
45569            this.hiddenCount++;
45570            this.autoSizeTabs();
45571         }
45572     },
45573
45574     /**
45575      * "Unhides" the {@link Roo.TabPanelItem} with the specified id/index.
45576      * @param {String/Number} id The id or index of the TabPanelItem to unhide.
45577      */
45578     unhideTab : function(id){
45579         var t = this.items[id];
45580         if(t.isHidden()){
45581            t.setHidden(false);
45582            this.hiddenCount--;
45583            this.autoSizeTabs();
45584         }
45585     },
45586
45587     /**
45588      * Adds an existing {@link Roo.TabPanelItem}.
45589      * @param {Roo.TabPanelItem} item The TabPanelItem to add
45590      */
45591     addTabItem : function(item)
45592     {
45593         this.items[item.id] = item;
45594         this.items.push(item);
45595         this.autoSizeTabs();
45596       //  if(this.resizeTabs){
45597     //       item.setWidth(this.currentTabWidth || this.preferredTabWidth);
45598   //         this.autoSizeTabs();
45599 //        }else{
45600 //            item.autoSize();
45601        // }
45602     },
45603
45604     /**
45605      * Removes a {@link Roo.TabPanelItem}.
45606      * @param {String/Number} id The id or index of the TabPanelItem to remove.
45607      */
45608     removeTab : function(id){
45609         var items = this.items;
45610         var tab = items[id];
45611         if(!tab) { return; }
45612         var index = items.indexOf(tab);
45613         if(this.active == tab && items.length > 1){
45614             var newTab = this.getNextAvailable(index);
45615             if(newTab) {
45616                 newTab.activate();
45617             }
45618         }
45619         this.stripEl.dom.removeChild(tab.pnode.dom);
45620         if(tab.bodyEl.dom.parentNode == this.bodyEl.dom){ // if it was moved already prevent error
45621             this.bodyEl.dom.removeChild(tab.bodyEl.dom);
45622         }
45623         items.splice(index, 1);
45624         delete this.items[tab.id];
45625         tab.fireEvent("close", tab);
45626         tab.purgeListeners();
45627         this.autoSizeTabs();
45628     },
45629
45630     getNextAvailable : function(start){
45631         var items = this.items;
45632         var index = start;
45633         // look for a next tab that will slide over to
45634         // replace the one being removed
45635         while(index < items.length){
45636             var item = items[++index];
45637             if(item && !item.isHidden()){
45638                 return item;
45639             }
45640         }
45641         // if one isn't found select the previous tab (on the left)
45642         index = start;
45643         while(index >= 0){
45644             var item = items[--index];
45645             if(item && !item.isHidden()){
45646                 return item;
45647             }
45648         }
45649         return null;
45650     },
45651
45652     /**
45653      * Disables a {@link Roo.TabPanelItem}. It cannot be the active tab, if it is this call is ignored.
45654      * @param {String/Number} id The id or index of the TabPanelItem to disable.
45655      */
45656     disableTab : function(id){
45657         var tab = this.items[id];
45658         if(tab && this.active != tab){
45659             tab.disable();
45660         }
45661     },
45662
45663     /**
45664      * Enables a {@link Roo.TabPanelItem} that is disabled.
45665      * @param {String/Number} id The id or index of the TabPanelItem to enable.
45666      */
45667     enableTab : function(id){
45668         var tab = this.items[id];
45669         tab.enable();
45670     },
45671
45672     /**
45673      * Activates a {@link Roo.TabPanelItem}. The currently active one will be deactivated.
45674      * @param {String/Number} id The id or index of the TabPanelItem to activate.
45675      * @return {Roo.TabPanelItem} The TabPanelItem.
45676      */
45677     activate : function(id)
45678     {
45679         //Roo.log('activite:'  + id);
45680         
45681         var tab = this.items[id];
45682         if(!tab){
45683             return null;
45684         }
45685         if(tab == this.active || tab.disabled){
45686             return tab;
45687         }
45688         var e = {};
45689         this.fireEvent("beforetabchange", this, e, tab);
45690         if(e.cancel !== true && !tab.disabled){
45691             if(this.active){
45692                 this.active.hide();
45693             }
45694             this.active = this.items[id];
45695             this.active.show();
45696             this.fireEvent("tabchange", this, this.active);
45697         }
45698         return tab;
45699     },
45700
45701     /**
45702      * Gets the active {@link Roo.TabPanelItem}.
45703      * @return {Roo.TabPanelItem} The active TabPanelItem or null if none are active.
45704      */
45705     getActiveTab : function(){
45706         return this.active;
45707     },
45708
45709     /**
45710      * Updates the tab body element to fit the height of the container element
45711      * for overflow scrolling
45712      * @param {Number} targetHeight (optional) Override the starting height from the elements height
45713      */
45714     syncHeight : function(targetHeight){
45715         var height = (targetHeight || this.el.getHeight())-this.el.getBorderWidth("tb")-this.el.getPadding("tb");
45716         var bm = this.bodyEl.getMargins();
45717         var newHeight = height-(this.stripWrap.getHeight()||0)-(bm.top+bm.bottom);
45718         this.bodyEl.setHeight(newHeight);
45719         return newHeight;
45720     },
45721
45722     onResize : function(){
45723         if(this.monitorResize){
45724             this.autoSizeTabs();
45725         }
45726     },
45727
45728     /**
45729      * Disables tab resizing while tabs are being added (if {@link #resizeTabs} is false this does nothing)
45730      */
45731     beginUpdate : function(){
45732         this.updating = true;
45733     },
45734
45735     /**
45736      * Stops an update and resizes the tabs (if {@link #resizeTabs} is false this does nothing)
45737      */
45738     endUpdate : function(){
45739         this.updating = false;
45740         this.autoSizeTabs();
45741     },
45742
45743     /**
45744      * Manual call to resize the tabs (if {@link #resizeTabs} is false this does nothing)
45745      */
45746     autoSizeTabs : function()
45747     {
45748         var count = this.items.length;
45749         var vcount = count - this.hiddenCount;
45750         
45751         if (vcount < 2) {
45752             this.stripEl.hide();
45753         } else {
45754             this.stripEl.show();
45755         }
45756         
45757         if(!this.resizeTabs || count < 1 || vcount < 1 || this.updating) {
45758             return;
45759         }
45760         
45761         
45762         var w = Math.max(this.el.getWidth() - this.cpad, 10);
45763         var availWidth = Math.floor(w / vcount);
45764         var b = this.stripBody;
45765         if(b.getWidth() > w){
45766             var tabs = this.items;
45767             this.setTabWidth(Math.max(availWidth, this.minTabWidth)-2);
45768             if(availWidth < this.minTabWidth){
45769                 /*if(!this.sleft){    // incomplete scrolling code
45770                     this.createScrollButtons();
45771                 }
45772                 this.showScroll();
45773                 this.stripClip.setWidth(w - (this.sleft.getWidth()+this.sright.getWidth()));*/
45774             }
45775         }else{
45776             if(this.currentTabWidth < this.preferredTabWidth){
45777                 this.setTabWidth(Math.min(availWidth, this.preferredTabWidth)-2);
45778             }
45779         }
45780     },
45781
45782     /**
45783      * Returns the number of tabs in this TabPanel.
45784      * @return {Number}
45785      */
45786      getCount : function(){
45787          return this.items.length;
45788      },
45789
45790     /**
45791      * Resizes all the tabs to the passed width
45792      * @param {Number} The new width
45793      */
45794     setTabWidth : function(width){
45795         this.currentTabWidth = width;
45796         for(var i = 0, len = this.items.length; i < len; i++) {
45797                 if(!this.items[i].isHidden()) {
45798                 this.items[i].setWidth(width);
45799             }
45800         }
45801     },
45802
45803     /**
45804      * Destroys this TabPanel
45805      * @param {Boolean} removeEl (optional) True to remove the element from the DOM as well (defaults to undefined)
45806      */
45807     destroy : function(removeEl){
45808         Roo.EventManager.removeResizeListener(this.onResize, this);
45809         for(var i = 0, len = this.items.length; i < len; i++){
45810             this.items[i].purgeListeners();
45811         }
45812         if(removeEl === true){
45813             this.el.update("");
45814             this.el.remove();
45815         }
45816     },
45817     
45818     createStrip : function(container)
45819     {
45820         var strip = document.createElement("nav");
45821         strip.className = Roo.bootstrap.version == 4 ?
45822             "navbar-light bg-light" : 
45823             "navbar navbar-default"; //"x-tabs-wrap";
45824         container.appendChild(strip);
45825         return strip;
45826     },
45827     
45828     createStripList : function(strip)
45829     {
45830         // div wrapper for retard IE
45831         // returns the "tr" element.
45832         strip.innerHTML = '<ul class="nav nav-tabs" role="tablist"></ul>';
45833         //'<div class="x-tabs-strip-wrap">'+
45834           //  '<table class="x-tabs-strip" cellspacing="0" cellpadding="0" border="0"><tbody><tr>'+
45835           //  '<td class="x-tab-strip-toolbar"></td></tr></tbody></table></div>';
45836         return strip.firstChild; //.firstChild.firstChild.firstChild;
45837     },
45838     createBody : function(container)
45839     {
45840         var body = document.createElement("div");
45841         Roo.id(body, "tab-body");
45842         //Roo.fly(body).addClass("x-tabs-body");
45843         Roo.fly(body).addClass("tab-content");
45844         container.appendChild(body);
45845         return body;
45846     },
45847     createItemBody :function(bodyEl, id){
45848         var body = Roo.getDom(id);
45849         if(!body){
45850             body = document.createElement("div");
45851             body.id = id;
45852         }
45853         //Roo.fly(body).addClass("x-tabs-item-body");
45854         Roo.fly(body).addClass("tab-pane");
45855          bodyEl.insertBefore(body, bodyEl.firstChild);
45856         return body;
45857     },
45858     /** @private */
45859     createStripElements :  function(stripEl, text, closable, tpl)
45860     {
45861         var td = document.createElement("li"); // was td..
45862         td.className = 'nav-item';
45863         
45864         //stripEl.insertBefore(td, stripEl.childNodes[stripEl.childNodes.length-1]);
45865         
45866         
45867         stripEl.appendChild(td);
45868         /*if(closable){
45869             td.className = "x-tabs-closable";
45870             if(!this.closeTpl){
45871                 this.closeTpl = new Roo.Template(
45872                    '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
45873                    '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span>' +
45874                    '<div unselectable="on" class="close-icon">&#160;</div></em></span></a>'
45875                 );
45876             }
45877             var el = this.closeTpl.overwrite(td, {"text": text});
45878             var close = el.getElementsByTagName("div")[0];
45879             var inner = el.getElementsByTagName("em")[0];
45880             return {"el": el, "close": close, "inner": inner};
45881         } else {
45882         */
45883         // not sure what this is..
45884 //            if(!this.tabTpl){
45885                 //this.tabTpl = new Roo.Template(
45886                 //   '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
45887                 //   '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span></em></span></a>'
45888                 //);
45889 //                this.tabTpl = new Roo.Template(
45890 //                   '<a href="#">' +
45891 //                   '<span unselectable="on"' +
45892 //                            (this.disableTooltips ? '' : ' title="{text}"') +
45893 //                            ' >{text}</span></a>'
45894 //                );
45895 //                
45896 //            }
45897
45898
45899             var template = tpl || this.tabTpl || false;
45900             
45901             if(!template){
45902                 template =  new Roo.Template(
45903                         Roo.bootstrap.version == 4 ? 
45904                             (
45905                                 '<a class="nav-link" href="#" unselectable="on"' +
45906                                      (this.disableTooltips ? '' : ' title="{text}"') +
45907                                      ' >{text}</a>'
45908                             ) : (
45909                                 '<a class="nav-link" href="#">' +
45910                                 '<span unselectable="on"' +
45911                                          (this.disableTooltips ? '' : ' title="{text}"') +
45912                                     ' >{text}</span></a>'
45913                             )
45914                 );
45915             }
45916             
45917             switch (typeof(template)) {
45918                 case 'object' :
45919                     break;
45920                 case 'string' :
45921                     template = new Roo.Template(template);
45922                     break;
45923                 default :
45924                     break;
45925             }
45926             
45927             var el = template.overwrite(td, {"text": text});
45928             
45929             var inner = el.getElementsByTagName("span")[0];
45930             
45931             return {"el": el, "inner": inner};
45932             
45933     }
45934         
45935     
45936 });
45937
45938 /**
45939  * @class Roo.TabPanelItem
45940  * @extends Roo.util.Observable
45941  * Represents an individual item (tab plus body) in a TabPanel.
45942  * @param {Roo.TabPanel} tabPanel The {@link Roo.TabPanel} this TabPanelItem belongs to
45943  * @param {String} id The id of this TabPanelItem
45944  * @param {String} text The text for the tab of this TabPanelItem
45945  * @param {Boolean} closable True to allow this TabPanelItem to be closable (defaults to false)
45946  */
45947 Roo.bootstrap.panel.TabItem = function(config){
45948     /**
45949      * The {@link Roo.TabPanel} this TabPanelItem belongs to
45950      * @type Roo.TabPanel
45951      */
45952     this.tabPanel = config.panel;
45953     /**
45954      * The id for this TabPanelItem
45955      * @type String
45956      */
45957     this.id = config.id;
45958     /** @private */
45959     this.disabled = false;
45960     /** @private */
45961     this.text = config.text;
45962     /** @private */
45963     this.loaded = false;
45964     this.closable = config.closable;
45965
45966     /**
45967      * The body element for this TabPanelItem.
45968      * @type Roo.Element
45969      */
45970     this.bodyEl = Roo.get(this.tabPanel.createItemBody(this.tabPanel.bodyEl.dom, config.id));
45971     this.bodyEl.setVisibilityMode(Roo.Element.VISIBILITY);
45972     this.bodyEl.setStyle("display", "block");
45973     this.bodyEl.setStyle("zoom", "1");
45974     //this.hideAction();
45975
45976     var els = this.tabPanel.createStripElements(this.tabPanel.stripEl.dom, config.text, config.closable, config.tpl);
45977     /** @private */
45978     this.el = Roo.get(els.el);
45979     this.inner = Roo.get(els.inner, true);
45980      this.textEl = Roo.bootstrap.version == 4 ?
45981         this.el : Roo.get(this.el.dom.firstChild, true);
45982
45983     this.pnode = this.linode = Roo.get(els.el.parentNode, true);
45984     this.status_node = Roo.bootstrap.version == 4 ? this.el : this.linode;
45985
45986     
45987 //    this.el.on("mousedown", this.onTabMouseDown, this);
45988     this.el.on("click", this.onTabClick, this);
45989     /** @private */
45990     if(config.closable){
45991         var c = Roo.get(els.close, true);
45992         c.dom.title = this.closeText;
45993         c.addClassOnOver("close-over");
45994         c.on("click", this.closeClick, this);
45995      }
45996
45997     this.addEvents({
45998          /**
45999          * @event activate
46000          * Fires when this tab becomes the active tab.
46001          * @param {Roo.TabPanel} tabPanel The parent TabPanel
46002          * @param {Roo.TabPanelItem} this
46003          */
46004         "activate": true,
46005         /**
46006          * @event beforeclose
46007          * Fires before this tab is closed. To cancel the close, set cancel to true on e (e.cancel = true).
46008          * @param {Roo.TabPanelItem} this
46009          * @param {Object} e Set cancel to true on this object to cancel the close.
46010          */
46011         "beforeclose": true,
46012         /**
46013          * @event close
46014          * Fires when this tab is closed.
46015          * @param {Roo.TabPanelItem} this
46016          */
46017          "close": true,
46018         /**
46019          * @event deactivate
46020          * Fires when this tab is no longer the active tab.
46021          * @param {Roo.TabPanel} tabPanel The parent TabPanel
46022          * @param {Roo.TabPanelItem} this
46023          */
46024          "deactivate" : true
46025     });
46026     this.hidden = false;
46027
46028     Roo.bootstrap.panel.TabItem.superclass.constructor.call(this);
46029 };
46030
46031 Roo.extend(Roo.bootstrap.panel.TabItem, Roo.util.Observable,
46032            {
46033     purgeListeners : function(){
46034        Roo.util.Observable.prototype.purgeListeners.call(this);
46035        this.el.removeAllListeners();
46036     },
46037     /**
46038      * Shows this TabPanelItem -- this <b>does not</b> deactivate the currently active TabPanelItem.
46039      */
46040     show : function(){
46041         this.status_node.addClass("active");
46042         this.showAction();
46043         if(Roo.isOpera){
46044             this.tabPanel.stripWrap.repaint();
46045         }
46046         this.fireEvent("activate", this.tabPanel, this);
46047     },
46048
46049     /**
46050      * Returns true if this tab is the active tab.
46051      * @return {Boolean}
46052      */
46053     isActive : function(){
46054         return this.tabPanel.getActiveTab() == this;
46055     },
46056
46057     /**
46058      * Hides this TabPanelItem -- if you don't activate another TabPanelItem this could look odd.
46059      */
46060     hide : function(){
46061         this.status_node.removeClass("active");
46062         this.hideAction();
46063         this.fireEvent("deactivate", this.tabPanel, this);
46064     },
46065
46066     hideAction : function(){
46067         this.bodyEl.hide();
46068         this.bodyEl.setStyle("position", "absolute");
46069         this.bodyEl.setLeft("-20000px");
46070         this.bodyEl.setTop("-20000px");
46071     },
46072
46073     showAction : function(){
46074         this.bodyEl.setStyle("position", "relative");
46075         this.bodyEl.setTop("");
46076         this.bodyEl.setLeft("");
46077         this.bodyEl.show();
46078     },
46079
46080     /**
46081      * Set the tooltip for the tab.
46082      * @param {String} tooltip The tab's tooltip
46083      */
46084     setTooltip : function(text){
46085         if(Roo.QuickTips && Roo.QuickTips.isEnabled()){
46086             this.textEl.dom.qtip = text;
46087             this.textEl.dom.removeAttribute('title');
46088         }else{
46089             this.textEl.dom.title = text;
46090         }
46091     },
46092
46093     onTabClick : function(e){
46094         e.preventDefault();
46095         this.tabPanel.activate(this.id);
46096     },
46097
46098     onTabMouseDown : function(e){
46099         e.preventDefault();
46100         this.tabPanel.activate(this.id);
46101     },
46102 /*
46103     getWidth : function(){
46104         return this.inner.getWidth();
46105     },
46106
46107     setWidth : function(width){
46108         var iwidth = width - this.linode.getPadding("lr");
46109         this.inner.setWidth(iwidth);
46110         this.textEl.setWidth(iwidth-this.inner.getPadding("lr"));
46111         this.linode.setWidth(width);
46112     },
46113 */
46114     /**
46115      * Show or hide the tab
46116      * @param {Boolean} hidden True to hide or false to show.
46117      */
46118     setHidden : function(hidden){
46119         this.hidden = hidden;
46120         this.linode.setStyle("display", hidden ? "none" : "");
46121     },
46122
46123     /**
46124      * Returns true if this tab is "hidden"
46125      * @return {Boolean}
46126      */
46127     isHidden : function(){
46128         return this.hidden;
46129     },
46130
46131     /**
46132      * Returns the text for this tab
46133      * @return {String}
46134      */
46135     getText : function(){
46136         return this.text;
46137     },
46138     /*
46139     autoSize : function(){
46140         //this.el.beginMeasure();
46141         this.textEl.setWidth(1);
46142         /*
46143          *  #2804 [new] Tabs in Roojs
46144          *  increase the width by 2-4 pixels to prevent the ellipssis showing in chrome
46145          */
46146         //this.setWidth(this.textEl.dom.scrollWidth+this.linode.getPadding("lr")+this.inner.getPadding("lr") + 2);
46147         //this.el.endMeasure();
46148     //},
46149
46150     /**
46151      * Sets the text for the tab (Note: this also sets the tooltip text)
46152      * @param {String} text The tab's text and tooltip
46153      */
46154     setText : function(text){
46155         this.text = text;
46156         this.textEl.update(text);
46157         this.setTooltip(text);
46158         //if(!this.tabPanel.resizeTabs){
46159         //    this.autoSize();
46160         //}
46161     },
46162     /**
46163      * Activates this TabPanelItem -- this <b>does</b> deactivate the currently active TabPanelItem.
46164      */
46165     activate : function(){
46166         this.tabPanel.activate(this.id);
46167     },
46168
46169     /**
46170      * Disables this TabPanelItem -- this does nothing if this is the active TabPanelItem.
46171      */
46172     disable : function(){
46173         if(this.tabPanel.active != this){
46174             this.disabled = true;
46175             this.status_node.addClass("disabled");
46176         }
46177     },
46178
46179     /**
46180      * Enables this TabPanelItem if it was previously disabled.
46181      */
46182     enable : function(){
46183         this.disabled = false;
46184         this.status_node.removeClass("disabled");
46185     },
46186
46187     /**
46188      * Sets the content for this TabPanelItem.
46189      * @param {String} content The content
46190      * @param {Boolean} loadScripts true to look for and load scripts
46191      */
46192     setContent : function(content, loadScripts){
46193         this.bodyEl.update(content, loadScripts);
46194     },
46195
46196     /**
46197      * Gets the {@link Roo.UpdateManager} for the body of this TabPanelItem. Enables you to perform Ajax updates.
46198      * @return {Roo.UpdateManager} The UpdateManager
46199      */
46200     getUpdateManager : function(){
46201         return this.bodyEl.getUpdateManager();
46202     },
46203
46204     /**
46205      * Set a URL to be used to load the content for this TabPanelItem.
46206      * @param {String/Function} url The URL to load the content from, or a function to call to get the URL
46207      * @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)
46208      * @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)
46209      * @return {Roo.UpdateManager} The UpdateManager
46210      */
46211     setUrl : function(url, params, loadOnce){
46212         if(this.refreshDelegate){
46213             this.un('activate', this.refreshDelegate);
46214         }
46215         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
46216         this.on("activate", this.refreshDelegate);
46217         return this.bodyEl.getUpdateManager();
46218     },
46219
46220     /** @private */
46221     _handleRefresh : function(url, params, loadOnce){
46222         if(!loadOnce || !this.loaded){
46223             var updater = this.bodyEl.getUpdateManager();
46224             updater.update(url, params, this._setLoaded.createDelegate(this));
46225         }
46226     },
46227
46228     /**
46229      *   Forces a content refresh from the URL specified in the {@link #setUrl} method.
46230      *   Will fail silently if the setUrl method has not been called.
46231      *   This does not activate the panel, just updates its content.
46232      */
46233     refresh : function(){
46234         if(this.refreshDelegate){
46235            this.loaded = false;
46236            this.refreshDelegate();
46237         }
46238     },
46239
46240     /** @private */
46241     _setLoaded : function(){
46242         this.loaded = true;
46243     },
46244
46245     /** @private */
46246     closeClick : function(e){
46247         var o = {};
46248         e.stopEvent();
46249         this.fireEvent("beforeclose", this, o);
46250         if(o.cancel !== true){
46251             this.tabPanel.removeTab(this.id);
46252         }
46253     },
46254     /**
46255      * The text displayed in the tooltip for the close icon.
46256      * @type String
46257      */
46258     closeText : "Close this tab"
46259 });
46260 /**
46261 *    This script refer to:
46262 *    Title: International Telephone Input
46263 *    Author: Jack O'Connor
46264 *    Code version:  v12.1.12
46265 *    Availability: https://github.com/jackocnr/intl-tel-input.git
46266 **/
46267
46268 Roo.bootstrap.form.PhoneInputData = function() {
46269     var d = [
46270       [
46271         "Afghanistan (‫افغانستان‬‎)",
46272         "af",
46273         "93"
46274       ],
46275       [
46276         "Albania (Shqipëri)",
46277         "al",
46278         "355"
46279       ],
46280       [
46281         "Algeria (‫الجزائر‬‎)",
46282         "dz",
46283         "213"
46284       ],
46285       [
46286         "American Samoa",
46287         "as",
46288         "1684"
46289       ],
46290       [
46291         "Andorra",
46292         "ad",
46293         "376"
46294       ],
46295       [
46296         "Angola",
46297         "ao",
46298         "244"
46299       ],
46300       [
46301         "Anguilla",
46302         "ai",
46303         "1264"
46304       ],
46305       [
46306         "Antigua and Barbuda",
46307         "ag",
46308         "1268"
46309       ],
46310       [
46311         "Argentina",
46312         "ar",
46313         "54"
46314       ],
46315       [
46316         "Armenia (Հայաստան)",
46317         "am",
46318         "374"
46319       ],
46320       [
46321         "Aruba",
46322         "aw",
46323         "297"
46324       ],
46325       [
46326         "Australia",
46327         "au",
46328         "61",
46329         0
46330       ],
46331       [
46332         "Austria (Österreich)",
46333         "at",
46334         "43"
46335       ],
46336       [
46337         "Azerbaijan (Azərbaycan)",
46338         "az",
46339         "994"
46340       ],
46341       [
46342         "Bahamas",
46343         "bs",
46344         "1242"
46345       ],
46346       [
46347         "Bahrain (‫البحرين‬‎)",
46348         "bh",
46349         "973"
46350       ],
46351       [
46352         "Bangladesh (বাংলাদেশ)",
46353         "bd",
46354         "880"
46355       ],
46356       [
46357         "Barbados",
46358         "bb",
46359         "1246"
46360       ],
46361       [
46362         "Belarus (Беларусь)",
46363         "by",
46364         "375"
46365       ],
46366       [
46367         "Belgium (België)",
46368         "be",
46369         "32"
46370       ],
46371       [
46372         "Belize",
46373         "bz",
46374         "501"
46375       ],
46376       [
46377         "Benin (Bénin)",
46378         "bj",
46379         "229"
46380       ],
46381       [
46382         "Bermuda",
46383         "bm",
46384         "1441"
46385       ],
46386       [
46387         "Bhutan (འབྲུག)",
46388         "bt",
46389         "975"
46390       ],
46391       [
46392         "Bolivia",
46393         "bo",
46394         "591"
46395       ],
46396       [
46397         "Bosnia and Herzegovina (Босна и Херцеговина)",
46398         "ba",
46399         "387"
46400       ],
46401       [
46402         "Botswana",
46403         "bw",
46404         "267"
46405       ],
46406       [
46407         "Brazil (Brasil)",
46408         "br",
46409         "55"
46410       ],
46411       [
46412         "British Indian Ocean Territory",
46413         "io",
46414         "246"
46415       ],
46416       [
46417         "British Virgin Islands",
46418         "vg",
46419         "1284"
46420       ],
46421       [
46422         "Brunei",
46423         "bn",
46424         "673"
46425       ],
46426       [
46427         "Bulgaria (България)",
46428         "bg",
46429         "359"
46430       ],
46431       [
46432         "Burkina Faso",
46433         "bf",
46434         "226"
46435       ],
46436       [
46437         "Burundi (Uburundi)",
46438         "bi",
46439         "257"
46440       ],
46441       [
46442         "Cambodia (កម្ពុជា)",
46443         "kh",
46444         "855"
46445       ],
46446       [
46447         "Cameroon (Cameroun)",
46448         "cm",
46449         "237"
46450       ],
46451       [
46452         "Canada",
46453         "ca",
46454         "1",
46455         1,
46456         ["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"]
46457       ],
46458       [
46459         "Cape Verde (Kabu Verdi)",
46460         "cv",
46461         "238"
46462       ],
46463       [
46464         "Caribbean Netherlands",
46465         "bq",
46466         "599",
46467         1
46468       ],
46469       [
46470         "Cayman Islands",
46471         "ky",
46472         "1345"
46473       ],
46474       [
46475         "Central African Republic (République centrafricaine)",
46476         "cf",
46477         "236"
46478       ],
46479       [
46480         "Chad (Tchad)",
46481         "td",
46482         "235"
46483       ],
46484       [
46485         "Chile",
46486         "cl",
46487         "56"
46488       ],
46489       [
46490         "China (中国)",
46491         "cn",
46492         "86"
46493       ],
46494       [
46495         "Christmas Island",
46496         "cx",
46497         "61",
46498         2
46499       ],
46500       [
46501         "Cocos (Keeling) Islands",
46502         "cc",
46503         "61",
46504         1
46505       ],
46506       [
46507         "Colombia",
46508         "co",
46509         "57"
46510       ],
46511       [
46512         "Comoros (‫جزر القمر‬‎)",
46513         "km",
46514         "269"
46515       ],
46516       [
46517         "Congo (DRC) (Jamhuri ya Kidemokrasia ya Kongo)",
46518         "cd",
46519         "243"
46520       ],
46521       [
46522         "Congo (Republic) (Congo-Brazzaville)",
46523         "cg",
46524         "242"
46525       ],
46526       [
46527         "Cook Islands",
46528         "ck",
46529         "682"
46530       ],
46531       [
46532         "Costa Rica",
46533         "cr",
46534         "506"
46535       ],
46536       [
46537         "Côte d’Ivoire",
46538         "ci",
46539         "225"
46540       ],
46541       [
46542         "Croatia (Hrvatska)",
46543         "hr",
46544         "385"
46545       ],
46546       [
46547         "Cuba",
46548         "cu",
46549         "53"
46550       ],
46551       [
46552         "Curaçao",
46553         "cw",
46554         "599",
46555         0
46556       ],
46557       [
46558         "Cyprus (Κύπρος)",
46559         "cy",
46560         "357"
46561       ],
46562       [
46563         "Czech Republic (Česká republika)",
46564         "cz",
46565         "420"
46566       ],
46567       [
46568         "Denmark (Danmark)",
46569         "dk",
46570         "45"
46571       ],
46572       [
46573         "Djibouti",
46574         "dj",
46575         "253"
46576       ],
46577       [
46578         "Dominica",
46579         "dm",
46580         "1767"
46581       ],
46582       [
46583         "Dominican Republic (República Dominicana)",
46584         "do",
46585         "1",
46586         2,
46587         ["809", "829", "849"]
46588       ],
46589       [
46590         "Ecuador",
46591         "ec",
46592         "593"
46593       ],
46594       [
46595         "Egypt (‫مصر‬‎)",
46596         "eg",
46597         "20"
46598       ],
46599       [
46600         "El Salvador",
46601         "sv",
46602         "503"
46603       ],
46604       [
46605         "Equatorial Guinea (Guinea Ecuatorial)",
46606         "gq",
46607         "240"
46608       ],
46609       [
46610         "Eritrea",
46611         "er",
46612         "291"
46613       ],
46614       [
46615         "Estonia (Eesti)",
46616         "ee",
46617         "372"
46618       ],
46619       [
46620         "Ethiopia",
46621         "et",
46622         "251"
46623       ],
46624       [
46625         "Falkland Islands (Islas Malvinas)",
46626         "fk",
46627         "500"
46628       ],
46629       [
46630         "Faroe Islands (Føroyar)",
46631         "fo",
46632         "298"
46633       ],
46634       [
46635         "Fiji",
46636         "fj",
46637         "679"
46638       ],
46639       [
46640         "Finland (Suomi)",
46641         "fi",
46642         "358",
46643         0
46644       ],
46645       [
46646         "France",
46647         "fr",
46648         "33"
46649       ],
46650       [
46651         "French Guiana (Guyane française)",
46652         "gf",
46653         "594"
46654       ],
46655       [
46656         "French Polynesia (Polynésie française)",
46657         "pf",
46658         "689"
46659       ],
46660       [
46661         "Gabon",
46662         "ga",
46663         "241"
46664       ],
46665       [
46666         "Gambia",
46667         "gm",
46668         "220"
46669       ],
46670       [
46671         "Georgia (საქართველო)",
46672         "ge",
46673         "995"
46674       ],
46675       [
46676         "Germany (Deutschland)",
46677         "de",
46678         "49"
46679       ],
46680       [
46681         "Ghana (Gaana)",
46682         "gh",
46683         "233"
46684       ],
46685       [
46686         "Gibraltar",
46687         "gi",
46688         "350"
46689       ],
46690       [
46691         "Greece (Ελλάδα)",
46692         "gr",
46693         "30"
46694       ],
46695       [
46696         "Greenland (Kalaallit Nunaat)",
46697         "gl",
46698         "299"
46699       ],
46700       [
46701         "Grenada",
46702         "gd",
46703         "1473"
46704       ],
46705       [
46706         "Guadeloupe",
46707         "gp",
46708         "590",
46709         0
46710       ],
46711       [
46712         "Guam",
46713         "gu",
46714         "1671"
46715       ],
46716       [
46717         "Guatemala",
46718         "gt",
46719         "502"
46720       ],
46721       [
46722         "Guernsey",
46723         "gg",
46724         "44",
46725         1
46726       ],
46727       [
46728         "Guinea (Guinée)",
46729         "gn",
46730         "224"
46731       ],
46732       [
46733         "Guinea-Bissau (Guiné Bissau)",
46734         "gw",
46735         "245"
46736       ],
46737       [
46738         "Guyana",
46739         "gy",
46740         "592"
46741       ],
46742       [
46743         "Haiti",
46744         "ht",
46745         "509"
46746       ],
46747       [
46748         "Honduras",
46749         "hn",
46750         "504"
46751       ],
46752       [
46753         "Hong Kong (香港)",
46754         "hk",
46755         "852"
46756       ],
46757       [
46758         "Hungary (Magyarország)",
46759         "hu",
46760         "36"
46761       ],
46762       [
46763         "Iceland (Ísland)",
46764         "is",
46765         "354"
46766       ],
46767       [
46768         "India (भारत)",
46769         "in",
46770         "91"
46771       ],
46772       [
46773         "Indonesia",
46774         "id",
46775         "62"
46776       ],
46777       [
46778         "Iran (‫ایران‬‎)",
46779         "ir",
46780         "98"
46781       ],
46782       [
46783         "Iraq (‫العراق‬‎)",
46784         "iq",
46785         "964"
46786       ],
46787       [
46788         "Ireland",
46789         "ie",
46790         "353"
46791       ],
46792       [
46793         "Isle of Man",
46794         "im",
46795         "44",
46796         2
46797       ],
46798       [
46799         "Israel (‫ישראל‬‎)",
46800         "il",
46801         "972"
46802       ],
46803       [
46804         "Italy (Italia)",
46805         "it",
46806         "39",
46807         0
46808       ],
46809       [
46810         "Jamaica",
46811         "jm",
46812         "1876"
46813       ],
46814       [
46815         "Japan (日本)",
46816         "jp",
46817         "81"
46818       ],
46819       [
46820         "Jersey",
46821         "je",
46822         "44",
46823         3
46824       ],
46825       [
46826         "Jordan (‫الأردن‬‎)",
46827         "jo",
46828         "962"
46829       ],
46830       [
46831         "Kazakhstan (Казахстан)",
46832         "kz",
46833         "7",
46834         1
46835       ],
46836       [
46837         "Kenya",
46838         "ke",
46839         "254"
46840       ],
46841       [
46842         "Kiribati",
46843         "ki",
46844         "686"
46845       ],
46846       [
46847         "Kosovo",
46848         "xk",
46849         "383"
46850       ],
46851       [
46852         "Kuwait (‫الكويت‬‎)",
46853         "kw",
46854         "965"
46855       ],
46856       [
46857         "Kyrgyzstan (Кыргызстан)",
46858         "kg",
46859         "996"
46860       ],
46861       [
46862         "Laos (ລາວ)",
46863         "la",
46864         "856"
46865       ],
46866       [
46867         "Latvia (Latvija)",
46868         "lv",
46869         "371"
46870       ],
46871       [
46872         "Lebanon (‫لبنان‬‎)",
46873         "lb",
46874         "961"
46875       ],
46876       [
46877         "Lesotho",
46878         "ls",
46879         "266"
46880       ],
46881       [
46882         "Liberia",
46883         "lr",
46884         "231"
46885       ],
46886       [
46887         "Libya (‫ليبيا‬‎)",
46888         "ly",
46889         "218"
46890       ],
46891       [
46892         "Liechtenstein",
46893         "li",
46894         "423"
46895       ],
46896       [
46897         "Lithuania (Lietuva)",
46898         "lt",
46899         "370"
46900       ],
46901       [
46902         "Luxembourg",
46903         "lu",
46904         "352"
46905       ],
46906       [
46907         "Macau (澳門)",
46908         "mo",
46909         "853"
46910       ],
46911       [
46912         "Macedonia (FYROM) (Македонија)",
46913         "mk",
46914         "389"
46915       ],
46916       [
46917         "Madagascar (Madagasikara)",
46918         "mg",
46919         "261"
46920       ],
46921       [
46922         "Malawi",
46923         "mw",
46924         "265"
46925       ],
46926       [
46927         "Malaysia",
46928         "my",
46929         "60"
46930       ],
46931       [
46932         "Maldives",
46933         "mv",
46934         "960"
46935       ],
46936       [
46937         "Mali",
46938         "ml",
46939         "223"
46940       ],
46941       [
46942         "Malta",
46943         "mt",
46944         "356"
46945       ],
46946       [
46947         "Marshall Islands",
46948         "mh",
46949         "692"
46950       ],
46951       [
46952         "Martinique",
46953         "mq",
46954         "596"
46955       ],
46956       [
46957         "Mauritania (‫موريتانيا‬‎)",
46958         "mr",
46959         "222"
46960       ],
46961       [
46962         "Mauritius (Moris)",
46963         "mu",
46964         "230"
46965       ],
46966       [
46967         "Mayotte",
46968         "yt",
46969         "262",
46970         1
46971       ],
46972       [
46973         "Mexico (México)",
46974         "mx",
46975         "52"
46976       ],
46977       [
46978         "Micronesia",
46979         "fm",
46980         "691"
46981       ],
46982       [
46983         "Moldova (Republica Moldova)",
46984         "md",
46985         "373"
46986       ],
46987       [
46988         "Monaco",
46989         "mc",
46990         "377"
46991       ],
46992       [
46993         "Mongolia (Монгол)",
46994         "mn",
46995         "976"
46996       ],
46997       [
46998         "Montenegro (Crna Gora)",
46999         "me",
47000         "382"
47001       ],
47002       [
47003         "Montserrat",
47004         "ms",
47005         "1664"
47006       ],
47007       [
47008         "Morocco (‫المغرب‬‎)",
47009         "ma",
47010         "212",
47011         0
47012       ],
47013       [
47014         "Mozambique (Moçambique)",
47015         "mz",
47016         "258"
47017       ],
47018       [
47019         "Myanmar (Burma) (မြန်မာ)",
47020         "mm",
47021         "95"
47022       ],
47023       [
47024         "Namibia (Namibië)",
47025         "na",
47026         "264"
47027       ],
47028       [
47029         "Nauru",
47030         "nr",
47031         "674"
47032       ],
47033       [
47034         "Nepal (नेपाल)",
47035         "np",
47036         "977"
47037       ],
47038       [
47039         "Netherlands (Nederland)",
47040         "nl",
47041         "31"
47042       ],
47043       [
47044         "New Caledonia (Nouvelle-Calédonie)",
47045         "nc",
47046         "687"
47047       ],
47048       [
47049         "New Zealand",
47050         "nz",
47051         "64"
47052       ],
47053       [
47054         "Nicaragua",
47055         "ni",
47056         "505"
47057       ],
47058       [
47059         "Niger (Nijar)",
47060         "ne",
47061         "227"
47062       ],
47063       [
47064         "Nigeria",
47065         "ng",
47066         "234"
47067       ],
47068       [
47069         "Niue",
47070         "nu",
47071         "683"
47072       ],
47073       [
47074         "Norfolk Island",
47075         "nf",
47076         "672"
47077       ],
47078       [
47079         "North Korea (조선 민주주의 인민 공화국)",
47080         "kp",
47081         "850"
47082       ],
47083       [
47084         "Northern Mariana Islands",
47085         "mp",
47086         "1670"
47087       ],
47088       [
47089         "Norway (Norge)",
47090         "no",
47091         "47",
47092         0
47093       ],
47094       [
47095         "Oman (‫عُمان‬‎)",
47096         "om",
47097         "968"
47098       ],
47099       [
47100         "Pakistan (‫پاکستان‬‎)",
47101         "pk",
47102         "92"
47103       ],
47104       [
47105         "Palau",
47106         "pw",
47107         "680"
47108       ],
47109       [
47110         "Palestine (‫فلسطين‬‎)",
47111         "ps",
47112         "970"
47113       ],
47114       [
47115         "Panama (Panamá)",
47116         "pa",
47117         "507"
47118       ],
47119       [
47120         "Papua New Guinea",
47121         "pg",
47122         "675"
47123       ],
47124       [
47125         "Paraguay",
47126         "py",
47127         "595"
47128       ],
47129       [
47130         "Peru (Perú)",
47131         "pe",
47132         "51"
47133       ],
47134       [
47135         "Philippines",
47136         "ph",
47137         "63"
47138       ],
47139       [
47140         "Poland (Polska)",
47141         "pl",
47142         "48"
47143       ],
47144       [
47145         "Portugal",
47146         "pt",
47147         "351"
47148       ],
47149       [
47150         "Puerto Rico",
47151         "pr",
47152         "1",
47153         3,
47154         ["787", "939"]
47155       ],
47156       [
47157         "Qatar (‫قطر‬‎)",
47158         "qa",
47159         "974"
47160       ],
47161       [
47162         "Réunion (La Réunion)",
47163         "re",
47164         "262",
47165         0
47166       ],
47167       [
47168         "Romania (România)",
47169         "ro",
47170         "40"
47171       ],
47172       [
47173         "Russia (Россия)",
47174         "ru",
47175         "7",
47176         0
47177       ],
47178       [
47179         "Rwanda",
47180         "rw",
47181         "250"
47182       ],
47183       [
47184         "Saint Barthélemy",
47185         "bl",
47186         "590",
47187         1
47188       ],
47189       [
47190         "Saint Helena",
47191         "sh",
47192         "290"
47193       ],
47194       [
47195         "Saint Kitts and Nevis",
47196         "kn",
47197         "1869"
47198       ],
47199       [
47200         "Saint Lucia",
47201         "lc",
47202         "1758"
47203       ],
47204       [
47205         "Saint Martin (Saint-Martin (partie française))",
47206         "mf",
47207         "590",
47208         2
47209       ],
47210       [
47211         "Saint Pierre and Miquelon (Saint-Pierre-et-Miquelon)",
47212         "pm",
47213         "508"
47214       ],
47215       [
47216         "Saint Vincent and the Grenadines",
47217         "vc",
47218         "1784"
47219       ],
47220       [
47221         "Samoa",
47222         "ws",
47223         "685"
47224       ],
47225       [
47226         "San Marino",
47227         "sm",
47228         "378"
47229       ],
47230       [
47231         "São Tomé and Príncipe (São Tomé e Príncipe)",
47232         "st",
47233         "239"
47234       ],
47235       [
47236         "Saudi Arabia (‫المملكة العربية السعودية‬‎)",
47237         "sa",
47238         "966"
47239       ],
47240       [
47241         "Senegal (Sénégal)",
47242         "sn",
47243         "221"
47244       ],
47245       [
47246         "Serbia (Србија)",
47247         "rs",
47248         "381"
47249       ],
47250       [
47251         "Seychelles",
47252         "sc",
47253         "248"
47254       ],
47255       [
47256         "Sierra Leone",
47257         "sl",
47258         "232"
47259       ],
47260       [
47261         "Singapore",
47262         "sg",
47263         "65"
47264       ],
47265       [
47266         "Sint Maarten",
47267         "sx",
47268         "1721"
47269       ],
47270       [
47271         "Slovakia (Slovensko)",
47272         "sk",
47273         "421"
47274       ],
47275       [
47276         "Slovenia (Slovenija)",
47277         "si",
47278         "386"
47279       ],
47280       [
47281         "Solomon Islands",
47282         "sb",
47283         "677"
47284       ],
47285       [
47286         "Somalia (Soomaaliya)",
47287         "so",
47288         "252"
47289       ],
47290       [
47291         "South Africa",
47292         "za",
47293         "27"
47294       ],
47295       [
47296         "South Korea (대한민국)",
47297         "kr",
47298         "82"
47299       ],
47300       [
47301         "South Sudan (‫جنوب السودان‬‎)",
47302         "ss",
47303         "211"
47304       ],
47305       [
47306         "Spain (España)",
47307         "es",
47308         "34"
47309       ],
47310       [
47311         "Sri Lanka (ශ්‍රී ලංකාව)",
47312         "lk",
47313         "94"
47314       ],
47315       [
47316         "Sudan (‫السودان‬‎)",
47317         "sd",
47318         "249"
47319       ],
47320       [
47321         "Suriname",
47322         "sr",
47323         "597"
47324       ],
47325       [
47326         "Svalbard and Jan Mayen",
47327         "sj",
47328         "47",
47329         1
47330       ],
47331       [
47332         "Swaziland",
47333         "sz",
47334         "268"
47335       ],
47336       [
47337         "Sweden (Sverige)",
47338         "se",
47339         "46"
47340       ],
47341       [
47342         "Switzerland (Schweiz)",
47343         "ch",
47344         "41"
47345       ],
47346       [
47347         "Syria (‫سوريا‬‎)",
47348         "sy",
47349         "963"
47350       ],
47351       [
47352         "Taiwan (台灣)",
47353         "tw",
47354         "886"
47355       ],
47356       [
47357         "Tajikistan",
47358         "tj",
47359         "992"
47360       ],
47361       [
47362         "Tanzania",
47363         "tz",
47364         "255"
47365       ],
47366       [
47367         "Thailand (ไทย)",
47368         "th",
47369         "66"
47370       ],
47371       [
47372         "Timor-Leste",
47373         "tl",
47374         "670"
47375       ],
47376       [
47377         "Togo",
47378         "tg",
47379         "228"
47380       ],
47381       [
47382         "Tokelau",
47383         "tk",
47384         "690"
47385       ],
47386       [
47387         "Tonga",
47388         "to",
47389         "676"
47390       ],
47391       [
47392         "Trinidad and Tobago",
47393         "tt",
47394         "1868"
47395       ],
47396       [
47397         "Tunisia (‫تونس‬‎)",
47398         "tn",
47399         "216"
47400       ],
47401       [
47402         "Turkey (Türkiye)",
47403         "tr",
47404         "90"
47405       ],
47406       [
47407         "Turkmenistan",
47408         "tm",
47409         "993"
47410       ],
47411       [
47412         "Turks and Caicos Islands",
47413         "tc",
47414         "1649"
47415       ],
47416       [
47417         "Tuvalu",
47418         "tv",
47419         "688"
47420       ],
47421       [
47422         "U.S. Virgin Islands",
47423         "vi",
47424         "1340"
47425       ],
47426       [
47427         "Uganda",
47428         "ug",
47429         "256"
47430       ],
47431       [
47432         "Ukraine (Україна)",
47433         "ua",
47434         "380"
47435       ],
47436       [
47437         "United Arab Emirates (‫الإمارات العربية المتحدة‬‎)",
47438         "ae",
47439         "971"
47440       ],
47441       [
47442         "United Kingdom",
47443         "gb",
47444         "44",
47445         0
47446       ],
47447       [
47448         "United States",
47449         "us",
47450         "1",
47451         0
47452       ],
47453       [
47454         "Uruguay",
47455         "uy",
47456         "598"
47457       ],
47458       [
47459         "Uzbekistan (Oʻzbekiston)",
47460         "uz",
47461         "998"
47462       ],
47463       [
47464         "Vanuatu",
47465         "vu",
47466         "678"
47467       ],
47468       [
47469         "Vatican City (Città del Vaticano)",
47470         "va",
47471         "39",
47472         1
47473       ],
47474       [
47475         "Venezuela",
47476         "ve",
47477         "58"
47478       ],
47479       [
47480         "Vietnam (Việt Nam)",
47481         "vn",
47482         "84"
47483       ],
47484       [
47485         "Wallis and Futuna (Wallis-et-Futuna)",
47486         "wf",
47487         "681"
47488       ],
47489       [
47490         "Western Sahara (‫الصحراء الغربية‬‎)",
47491         "eh",
47492         "212",
47493         1
47494       ],
47495       [
47496         "Yemen (‫اليمن‬‎)",
47497         "ye",
47498         "967"
47499       ],
47500       [
47501         "Zambia",
47502         "zm",
47503         "260"
47504       ],
47505       [
47506         "Zimbabwe",
47507         "zw",
47508         "263"
47509       ],
47510       [
47511         "Åland Islands",
47512         "ax",
47513         "358",
47514         1
47515       ]
47516   ];
47517   
47518   return d;
47519 }/**
47520 *    This script refer to:
47521 *    Title: International Telephone Input
47522 *    Author: Jack O'Connor
47523 *    Code version:  v12.1.12
47524 *    Availability: https://github.com/jackocnr/intl-tel-input.git
47525 **/
47526
47527 /**
47528  * @class Roo.bootstrap.form.PhoneInput
47529  * @extends Roo.bootstrap.form.TriggerField
47530  * An input with International dial-code selection
47531  
47532  * @cfg {String} defaultDialCode default '+852'
47533  * @cfg {Array} preferedCountries default []
47534   
47535  * @constructor
47536  * Create a new PhoneInput.
47537  * @param {Object} config Configuration options
47538  */
47539
47540 Roo.bootstrap.form.PhoneInput = function(config) {
47541     Roo.bootstrap.form.PhoneInput.superclass.constructor.call(this, config);
47542 };
47543
47544 Roo.extend(Roo.bootstrap.form.PhoneInput, Roo.bootstrap.form.TriggerField, {
47545         /**
47546         * @cfg {Roo.data.Store} store [required] The data store to which this combo is bound (defaults to undefined)
47547         */
47548         listWidth: undefined,
47549         
47550         selectedClass: 'active',
47551         
47552         invalidClass : "has-warning",
47553         
47554         validClass: 'has-success',
47555         
47556         allowed: '0123456789',
47557         
47558         max_length: 15,
47559         
47560         /**
47561          * @cfg {String} defaultDialCode The default dial code when initializing the input
47562          */
47563         defaultDialCode: '+852',
47564         
47565         /**
47566          * @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
47567          */
47568         preferedCountries: false,
47569         
47570         getAutoCreate : function()
47571         {
47572             var data = Roo.bootstrap.form.PhoneInputData();
47573             var align = this.labelAlign || this.parentLabelAlign();
47574             var id = Roo.id();
47575             
47576             this.allCountries = [];
47577             this.dialCodeMapping = [];
47578             
47579             for (var i = 0; i < data.length; i++) {
47580               var c = data[i];
47581               this.allCountries[i] = {
47582                 name: c[0],
47583                 iso2: c[1],
47584                 dialCode: c[2],
47585                 priority: c[3] || 0,
47586                 areaCodes: c[4] || null
47587               };
47588               this.dialCodeMapping[c[2]] = {
47589                   name: c[0],
47590                   iso2: c[1],
47591                   priority: c[3] || 0,
47592                   areaCodes: c[4] || null
47593               };
47594             }
47595             
47596             var cfg = {
47597                 cls: 'form-group',
47598                 cn: []
47599             };
47600             
47601             var input =  {
47602                 tag: 'input',
47603                 id : id,
47604                 // type: 'number', -- do not use number - we get the flaky up/down arrows.
47605                 maxlength: this.max_length,
47606                 cls : 'form-control tel-input',
47607                 autocomplete: 'new-password'
47608             };
47609             
47610             var hiddenInput = {
47611                 tag: 'input',
47612                 type: 'hidden',
47613                 cls: 'hidden-tel-input'
47614             };
47615             
47616             if (this.name) {
47617                 hiddenInput.name = this.name;
47618             }
47619             
47620             if (this.disabled) {
47621                 input.disabled = true;
47622             }
47623             
47624             var flag_container = {
47625                 tag: 'div',
47626                 cls: 'flag-box',
47627                 cn: [
47628                     {
47629                         tag: 'div',
47630                         cls: 'flag'
47631                     },
47632                     {
47633                         tag: 'div',
47634                         cls: 'caret'
47635                     }
47636                 ]
47637             };
47638             
47639             var box = {
47640                 tag: 'div',
47641                 cls: this.hasFeedback ? 'has-feedback' : '',
47642                 cn: [
47643                     hiddenInput,
47644                     input,
47645                     {
47646                         tag: 'input',
47647                         cls: 'dial-code-holder',
47648                         disabled: true
47649                     }
47650                 ]
47651             };
47652             
47653             var container = {
47654                 cls: 'roo-select2-container input-group',
47655                 cn: [
47656                     flag_container,
47657                     box
47658                 ]
47659             };
47660             
47661             if (this.fieldLabel.length) {
47662                 var indicator = {
47663                     tag: 'i',
47664                     tooltip: 'This field is required'
47665                 };
47666                 
47667                 var label = {
47668                     tag: 'label',
47669                     'for':  id,
47670                     cls: 'control-label',
47671                     cn: []
47672                 };
47673                 
47674                 var label_text = {
47675                     tag: 'span',
47676                     html: this.fieldLabel
47677                 };
47678                 
47679                 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star left-indicator';
47680                 label.cn = [
47681                     indicator,
47682                     label_text
47683                 ];
47684                 
47685                 if(this.indicatorpos == 'right') {
47686                     indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star right-indicator';
47687                     label.cn = [
47688                         label_text,
47689                         indicator
47690                     ];
47691                 }
47692                 
47693                 if(align == 'left') {
47694                     container = {
47695                         tag: 'div',
47696                         cn: [
47697                             container
47698                         ]
47699                     };
47700                     
47701                     if(this.labelWidth > 12){
47702                         label.style = "width: " + this.labelWidth + 'px';
47703                     }
47704                     if(this.labelWidth < 13 && this.labelmd == 0){
47705                         this.labelmd = this.labelWidth;
47706                     }
47707                     if(this.labellg > 0){
47708                         label.cls += ' col-lg-' + this.labellg;
47709                         input.cls += ' col-lg-' + (12 - this.labellg);
47710                     }
47711                     if(this.labelmd > 0){
47712                         label.cls += ' col-md-' + this.labelmd;
47713                         container.cls += ' col-md-' + (12 - this.labelmd);
47714                     }
47715                     if(this.labelsm > 0){
47716                         label.cls += ' col-sm-' + this.labelsm;
47717                         container.cls += ' col-sm-' + (12 - this.labelsm);
47718                     }
47719                     if(this.labelxs > 0){
47720                         label.cls += ' col-xs-' + this.labelxs;
47721                         container.cls += ' col-xs-' + (12 - this.labelxs);
47722                     }
47723                 }
47724             }
47725             
47726             cfg.cn = [
47727                 label,
47728                 container
47729             ];
47730             
47731             var settings = this;
47732             
47733             ['xs','sm','md','lg'].map(function(size){
47734                 if (settings[size]) {
47735                     cfg.cls += ' col-' + size + '-' + settings[size];
47736                 }
47737             });
47738             
47739             this.store = new Roo.data.Store({
47740                 proxy : new Roo.data.MemoryProxy({}),
47741                 reader : new Roo.data.JsonReader({
47742                     fields : [
47743                         {
47744                             'name' : 'name',
47745                             'type' : 'string'
47746                         },
47747                         {
47748                             'name' : 'iso2',
47749                             'type' : 'string'
47750                         },
47751                         {
47752                             'name' : 'dialCode',
47753                             'type' : 'string'
47754                         },
47755                         {
47756                             'name' : 'priority',
47757                             'type' : 'string'
47758                         },
47759                         {
47760                             'name' : 'areaCodes',
47761                             'type' : 'string'
47762                         }
47763                     ]
47764                 })
47765             });
47766             
47767             if(!this.preferedCountries) {
47768                 this.preferedCountries = [
47769                     'hk',
47770                     'gb',
47771                     'us'
47772                 ];
47773             }
47774             
47775             var p = this.preferedCountries.reverse();
47776             
47777             if(p) {
47778                 for (var i = 0; i < p.length; i++) {
47779                     for (var j = 0; j < this.allCountries.length; j++) {
47780                         if(this.allCountries[j].iso2 == p[i]) {
47781                             var t = this.allCountries[j];
47782                             this.allCountries.splice(j,1);
47783                             this.allCountries.unshift(t);
47784                         }
47785                     } 
47786                 }
47787             }
47788             
47789             this.store.proxy.data = {
47790                 success: true,
47791                 data: this.allCountries
47792             };
47793             
47794             return cfg;
47795         },
47796         
47797         initEvents : function()
47798         {
47799             this.createList();
47800             Roo.bootstrap.form.PhoneInput.superclass.initEvents.call(this);
47801             
47802             this.indicator = this.indicatorEl();
47803             this.flag = this.flagEl();
47804             this.dialCodeHolder = this.dialCodeHolderEl();
47805             
47806             this.trigger = this.el.select('div.flag-box',true).first();
47807             this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
47808             
47809             var _this = this;
47810             
47811             (function(){
47812                 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
47813                 _this.list.setWidth(lw);
47814             }).defer(100);
47815             
47816             this.list.on('mouseover', this.onViewOver, this);
47817             this.list.on('mousemove', this.onViewMove, this);
47818             this.inputEl().on("keyup", this.onKeyUp, this);
47819             this.inputEl().on("keypress", this.onKeyPress, this);
47820             
47821             this.tpl = '<li><a href="#"><div class="flag {iso2}"></div>{name} <span class="dial-code">+{dialCode}</span></a></li>';
47822
47823             this.view = new Roo.View(this.list, this.tpl, {
47824                 singleSelect:true, store: this.store, selectedClass: this.selectedClass
47825             });
47826             
47827             this.view.on('click', this.onViewClick, this);
47828             this.setValue(this.defaultDialCode);
47829         },
47830         
47831         onTriggerClick : function(e)
47832         {
47833             Roo.log('trigger click');
47834             if(this.disabled){
47835                 return;
47836             }
47837             
47838             if(this.isExpanded()){
47839                 this.collapse();
47840                 this.hasFocus = false;
47841             }else {
47842                 this.store.load({});
47843                 this.hasFocus = true;
47844                 this.expand();
47845             }
47846         },
47847         
47848         isExpanded : function()
47849         {
47850             return this.list.isVisible();
47851         },
47852         
47853         collapse : function()
47854         {
47855             if(!this.isExpanded()){
47856                 return;
47857             }
47858             this.list.hide();
47859             Roo.get(document).un('mousedown', this.collapseIf, this);
47860             Roo.get(document).un('mousewheel', this.collapseIf, this);
47861             this.fireEvent('collapse', this);
47862             this.validate();
47863         },
47864         
47865         expand : function()
47866         {
47867             Roo.log('expand');
47868
47869             if(this.isExpanded() || !this.hasFocus){
47870                 return;
47871             }
47872             
47873             var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
47874             this.list.setWidth(lw);
47875             
47876             this.list.show();
47877             this.restrictHeight();
47878             
47879             Roo.get(document).on('mousedown', this.collapseIf, this);
47880             Roo.get(document).on('mousewheel', this.collapseIf, this);
47881             
47882             this.fireEvent('expand', this);
47883         },
47884         
47885         restrictHeight : function()
47886         {
47887             this.list.alignTo(this.inputEl(), this.listAlign);
47888             this.list.alignTo(this.inputEl(), this.listAlign);
47889         },
47890         
47891         onViewOver : function(e, t)
47892         {
47893             if(this.inKeyMode){
47894                 return;
47895             }
47896             var item = this.view.findItemFromChild(t);
47897             
47898             if(item){
47899                 var index = this.view.indexOf(item);
47900                 this.select(index, false);
47901             }
47902         },
47903
47904         // private
47905         onViewClick : function(view, doFocus, el, e)
47906         {
47907             var index = this.view.getSelectedIndexes()[0];
47908             
47909             var r = this.store.getAt(index);
47910             
47911             if(r){
47912                 this.onSelect(r, index);
47913             }
47914             if(doFocus !== false && !this.blockFocus){
47915                 this.inputEl().focus();
47916             }
47917         },
47918         
47919         onViewMove : function(e, t)
47920         {
47921             this.inKeyMode = false;
47922         },
47923         
47924         select : function(index, scrollIntoView)
47925         {
47926             this.selectedIndex = index;
47927             this.view.select(index);
47928             if(scrollIntoView !== false){
47929                 var el = this.view.getNode(index);
47930                 if(el){
47931                     this.list.scrollChildIntoView(el, false);
47932                 }
47933             }
47934         },
47935         
47936         createList : function()
47937         {
47938             this.list = Roo.get(document.body).createChild({
47939                 tag: 'ul',
47940                 cls: 'typeahead typeahead-long dropdown-menu tel-list',
47941                 style: 'display:none'
47942             });
47943             
47944             this.list.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
47945         },
47946         
47947         collapseIf : function(e)
47948         {
47949             var in_combo  = e.within(this.el);
47950             var in_list =  e.within(this.list);
47951             var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
47952             
47953             if (in_combo || in_list || is_list) {
47954                 return;
47955             }
47956             this.collapse();
47957         },
47958         
47959         onSelect : function(record, index)
47960         {
47961             if(this.fireEvent('beforeselect', this, record, index) !== false){
47962                 
47963                 this.setFlagClass(record.data.iso2);
47964                 this.setDialCode(record.data.dialCode);
47965                 this.hasFocus = false;
47966                 this.collapse();
47967                 this.fireEvent('select', this, record, index);
47968             }
47969         },
47970         
47971         flagEl : function()
47972         {
47973             var flag = this.el.select('div.flag',true).first();
47974             if(!flag){
47975                 return false;
47976             }
47977             return flag;
47978         },
47979         
47980         dialCodeHolderEl : function()
47981         {
47982             var d = this.el.select('input.dial-code-holder',true).first();
47983             if(!d){
47984                 return false;
47985             }
47986             return d;
47987         },
47988         
47989         setDialCode : function(v)
47990         {
47991             this.dialCodeHolder.dom.value = '+'+v;
47992         },
47993         
47994         setFlagClass : function(n)
47995         {
47996             this.flag.dom.className = 'flag '+n;
47997         },
47998         
47999         getValue : function()
48000         {
48001             var v = this.inputEl().getValue();
48002             if(this.dialCodeHolder) {
48003                 v = this.dialCodeHolder.dom.value+this.inputEl().getValue();
48004             }
48005             return v;
48006         },
48007         
48008         setValue : function(v)
48009         {
48010             var d = this.getDialCode(v);
48011             
48012             //invalid dial code
48013             if(v.length == 0 || !d || d.length == 0) {
48014                 if(this.rendered){
48015                     this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
48016                     this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
48017                 }
48018                 return;
48019             }
48020             
48021             //valid dial code
48022             this.setFlagClass(this.dialCodeMapping[d].iso2);
48023             this.setDialCode(d);
48024             this.inputEl().dom.value = v.replace('+'+d,'');
48025             this.hiddenEl().dom.value = this.getValue();
48026             
48027             this.validate();
48028         },
48029         
48030         getDialCode : function(v)
48031         {
48032             v = v ||  '';
48033             
48034             if (v.length == 0) {
48035                 return this.dialCodeHolder.dom.value;
48036             }
48037             
48038             var dialCode = "";
48039             if (v.charAt(0) != "+") {
48040                 return false;
48041             }
48042             var numericChars = "";
48043             for (var i = 1; i < v.length; i++) {
48044               var c = v.charAt(i);
48045               if (!isNaN(c)) {
48046                 numericChars += c;
48047                 if (this.dialCodeMapping[numericChars]) {
48048                   dialCode = v.substr(1, i);
48049                 }
48050                 if (numericChars.length == 4) {
48051                   break;
48052                 }
48053               }
48054             }
48055             return dialCode;
48056         },
48057         
48058         reset : function()
48059         {
48060             this.setValue(this.defaultDialCode);
48061             this.validate();
48062         },
48063         
48064         hiddenEl : function()
48065         {
48066             return this.el.select('input.hidden-tel-input',true).first();
48067         },
48068         
48069         // after setting val
48070         onKeyUp : function(e){
48071             this.setValue(this.getValue());
48072         },
48073         
48074         onKeyPress : function(e){
48075             if(this.allowed.indexOf(String.fromCharCode(e.getCharCode())) === -1){
48076                 e.stopEvent();
48077             }
48078         }
48079         
48080 });
48081 /**
48082  * @class Roo.bootstrap.form.MoneyField
48083  * @extends Roo.bootstrap.form.ComboBox
48084  * Bootstrap MoneyField class
48085  * 
48086  * @constructor
48087  * Create a new MoneyField.
48088  * @param {Object} config Configuration options
48089  */
48090
48091 Roo.bootstrap.form.MoneyField = function(config) {
48092     
48093     Roo.bootstrap.form.MoneyField.superclass.constructor.call(this, config);
48094     
48095 };
48096
48097 Roo.extend(Roo.bootstrap.form.MoneyField, Roo.bootstrap.form.ComboBox, {
48098     
48099     /**
48100      * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
48101      */
48102     allowDecimals : true,
48103     /**
48104      * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
48105      */
48106     decimalSeparator : ".",
48107     /**
48108      * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
48109      */
48110     decimalPrecision : 0,
48111     /**
48112      * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
48113      */
48114     allowNegative : true,
48115     /**
48116      * @cfg {Boolean} allowZero False to blank out if the user enters '0' (defaults to true)
48117      */
48118     allowZero: true,
48119     /**
48120      * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
48121      */
48122     minValue : Number.NEGATIVE_INFINITY,
48123     /**
48124      * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
48125      */
48126     maxValue : Number.MAX_VALUE,
48127     /**
48128      * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
48129      */
48130     minText : "The minimum value for this field is {0}",
48131     /**
48132      * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
48133      */
48134     maxText : "The maximum value for this field is {0}",
48135     /**
48136      * @cfg {String} nanText Error text to display if the value is not a valid number.  For example, this can happen
48137      * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
48138      */
48139     nanText : "{0} is not a valid number",
48140     /**
48141      * @cfg {Boolean} castInt (true|false) cast int if true (defalut true)
48142      */
48143     castInt : true,
48144     /**
48145      * @cfg {String} defaults currency of the MoneyField
48146      * value should be in lkey
48147      */
48148     defaultCurrency : false,
48149     /**
48150      * @cfg {String} thousandsDelimiter Symbol of thousandsDelimiter
48151      */
48152     thousandsDelimiter : false,
48153     /**
48154      * @cfg {Number} max_length Maximum input field length allowed (defaults to Number.MAX_VALUE)
48155      */
48156     max_length: false,
48157     
48158     inputlg : 9,
48159     inputmd : 9,
48160     inputsm : 9,
48161     inputxs : 6,
48162      /**
48163      * @cfg {Roo.data.Store} store  Store to lookup currency??
48164      */
48165     store : false,
48166     
48167     getAutoCreate : function()
48168     {
48169         var align = this.labelAlign || this.parentLabelAlign();
48170         
48171         var id = Roo.id();
48172
48173         var cfg = {
48174             cls: 'form-group',
48175             cn: []
48176         };
48177
48178         var input =  {
48179             tag: 'input',
48180             id : id,
48181             cls : 'form-control roo-money-amount-input',
48182             autocomplete: 'new-password'
48183         };
48184         
48185         var hiddenInput = {
48186             tag: 'input',
48187             type: 'hidden',
48188             id: Roo.id(),
48189             cls: 'hidden-number-input'
48190         };
48191         
48192         if(this.max_length) {
48193             input.maxlength = this.max_length; 
48194         }
48195         
48196         if (this.name) {
48197             hiddenInput.name = this.name;
48198         }
48199
48200         if (this.disabled) {
48201             input.disabled = true;
48202         }
48203
48204         var clg = 12 - this.inputlg;
48205         var cmd = 12 - this.inputmd;
48206         var csm = 12 - this.inputsm;
48207         var cxs = 12 - this.inputxs;
48208         
48209         var container = {
48210             tag : 'div',
48211             cls : 'row roo-money-field',
48212             cn : [
48213                 {
48214                     tag : 'div',
48215                     cls : 'roo-money-currency column col-lg-' + clg + ' col-md-' + cmd + ' col-sm-' + csm + ' col-xs-' + cxs,
48216                     cn : [
48217                         {
48218                             tag : 'div',
48219                             cls: 'roo-select2-container input-group',
48220                             cn: [
48221                                 {
48222                                     tag : 'input',
48223                                     cls : 'form-control roo-money-currency-input',
48224                                     autocomplete: 'new-password',
48225                                     readOnly : 1,
48226                                     name : this.currencyName
48227                                 },
48228                                 {
48229                                     tag :'span',
48230                                     cls : 'input-group-addon',
48231                                     cn : [
48232                                         {
48233                                             tag: 'span',
48234                                             cls: 'caret'
48235                                         }
48236                                     ]
48237                                 }
48238                             ]
48239                         }
48240                     ]
48241                 },
48242                 {
48243                     tag : 'div',
48244                     cls : 'roo-money-amount column col-lg-' + this.inputlg + ' col-md-' + this.inputmd + ' col-sm-' + this.inputsm + ' col-xs-' + this.inputxs,
48245                     cn : [
48246                         {
48247                             tag: 'div',
48248                             cls: this.hasFeedback ? 'has-feedback' : '',
48249                             cn: [
48250                                 input
48251                             ]
48252                         }
48253                     ]
48254                 }
48255             ]
48256             
48257         };
48258         
48259         if (this.fieldLabel.length) {
48260             var indicator = {
48261                 tag: 'i',
48262                 tooltip: 'This field is required'
48263             };
48264
48265             var label = {
48266                 tag: 'label',
48267                 'for':  id,
48268                 cls: 'control-label',
48269                 cn: []
48270             };
48271
48272             var label_text = {
48273                 tag: 'span',
48274                 html: this.fieldLabel
48275             };
48276
48277             indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star left-indicator';
48278             label.cn = [
48279                 indicator,
48280                 label_text
48281             ];
48282
48283             if(this.indicatorpos == 'right') {
48284                 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star right-indicator';
48285                 label.cn = [
48286                     label_text,
48287                     indicator
48288                 ];
48289             }
48290
48291             if(align == 'left') {
48292                 container = {
48293                     tag: 'div',
48294                     cn: [
48295                         container
48296                     ]
48297                 };
48298
48299                 if(this.labelWidth > 12){
48300                     label.style = "width: " + this.labelWidth + 'px';
48301                 }
48302                 if(this.labelWidth < 13 && this.labelmd == 0){
48303                     this.labelmd = this.labelWidth;
48304                 }
48305                 if(this.labellg > 0){
48306                     label.cls += ' col-lg-' + this.labellg;
48307                     input.cls += ' col-lg-' + (12 - this.labellg);
48308                 }
48309                 if(this.labelmd > 0){
48310                     label.cls += ' col-md-' + this.labelmd;
48311                     container.cls += ' col-md-' + (12 - this.labelmd);
48312                 }
48313                 if(this.labelsm > 0){
48314                     label.cls += ' col-sm-' + this.labelsm;
48315                     container.cls += ' col-sm-' + (12 - this.labelsm);
48316                 }
48317                 if(this.labelxs > 0){
48318                     label.cls += ' col-xs-' + this.labelxs;
48319                     container.cls += ' col-xs-' + (12 - this.labelxs);
48320                 }
48321             }
48322         }
48323
48324         cfg.cn = [
48325             label,
48326             container,
48327             hiddenInput
48328         ];
48329         
48330         var settings = this;
48331
48332         ['xs','sm','md','lg'].map(function(size){
48333             if (settings[size]) {
48334                 cfg.cls += ' col-' + size + '-' + settings[size];
48335             }
48336         });
48337         
48338         return cfg;
48339     },
48340     
48341     initEvents : function()
48342     {
48343         this.indicator = this.indicatorEl();
48344         
48345         this.initCurrencyEvent();
48346         
48347         this.initNumberEvent();
48348     },
48349     
48350     initCurrencyEvent : function()
48351     {
48352         if (!this.store) {
48353             throw "can not find store for combo";
48354         }
48355         
48356         this.store = Roo.factory(this.store, Roo.data);
48357         this.store.parent = this;
48358         
48359         this.createList();
48360         
48361         this.triggerEl = this.el.select('.input-group-addon', true).first();
48362         
48363         this.triggerEl.on("click", this.onTriggerClick, this, { preventDefault : true });
48364         
48365         var _this = this;
48366         
48367         (function(){
48368             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
48369             _this.list.setWidth(lw);
48370         }).defer(100);
48371         
48372         this.list.on('mouseover', this.onViewOver, this);
48373         this.list.on('mousemove', this.onViewMove, this);
48374         this.list.on('scroll', this.onViewScroll, this);
48375         
48376         if(!this.tpl){
48377             this.tpl = '<li><a href="#">{' + this.currencyField + '}</a></li>';
48378         }
48379         
48380         this.view = new Roo.View(this.list, this.tpl, {
48381             singleSelect:true, store: this.store, selectedClass: this.selectedClass
48382         });
48383         
48384         this.view.on('click', this.onViewClick, this);
48385         
48386         this.store.on('beforeload', this.onBeforeLoad, this);
48387         this.store.on('load', this.onLoad, this);
48388         this.store.on('loadexception', this.onLoadException, this);
48389         
48390         this.keyNav = new Roo.KeyNav(this.currencyEl(), {
48391             "up" : function(e){
48392                 this.inKeyMode = true;
48393                 this.selectPrev();
48394             },
48395
48396             "down" : function(e){
48397                 if(!this.isExpanded()){
48398                     this.onTriggerClick();
48399                 }else{
48400                     this.inKeyMode = true;
48401                     this.selectNext();
48402                 }
48403             },
48404
48405             "enter" : function(e){
48406                 this.collapse();
48407                 
48408                 if(this.fireEvent("specialkey", this, e)){
48409                     this.onViewClick(false);
48410                 }
48411                 
48412                 return true;
48413             },
48414
48415             "esc" : function(e){
48416                 this.collapse();
48417             },
48418
48419             "tab" : function(e){
48420                 this.collapse();
48421                 
48422                 if(this.fireEvent("specialkey", this, e)){
48423                     this.onViewClick(false);
48424                 }
48425                 
48426                 return true;
48427             },
48428
48429             scope : this,
48430
48431             doRelay : function(foo, bar, hname){
48432                 if(hname == 'down' || this.scope.isExpanded()){
48433                    return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
48434                 }
48435                 return true;
48436             },
48437
48438             forceKeyDown: true
48439         });
48440         
48441         this.currencyEl().on("click", this.onTriggerClick, this, { preventDefault : true });
48442         
48443     },
48444     
48445     initNumberEvent : function(e)
48446     {
48447         this.inputEl().on("keydown" , this.fireKey,  this);
48448         this.inputEl().on("focus", this.onFocus,  this);
48449         this.inputEl().on("blur", this.onBlur,  this);
48450         
48451         this.inputEl().relayEvent('keyup', this);
48452         
48453         if(this.indicator){
48454             this.indicator.addClass('invisible');
48455         }
48456  
48457         this.originalValue = this.getValue();
48458         
48459         if(this.validationEvent == 'keyup'){
48460             this.validationTask = new Roo.util.DelayedTask(this.validate, this);
48461             this.inputEl().on('keyup', this.filterValidation, this);
48462         }
48463         else if(this.validationEvent !== false){
48464             this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
48465         }
48466         
48467         if(this.selectOnFocus){
48468             this.on("focus", this.preFocus, this);
48469             
48470         }
48471         if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
48472             this.inputEl().on("keypress", this.filterKeys, this);
48473         } else {
48474             this.inputEl().relayEvent('keypress', this);
48475         }
48476         
48477         var allowed = "0123456789";
48478         
48479         if(this.allowDecimals){
48480             allowed += this.decimalSeparator;
48481         }
48482         
48483         if(this.allowNegative){
48484             allowed += "-";
48485         }
48486         
48487         if(this.thousandsDelimiter) {
48488             allowed += ",";
48489         }
48490         
48491         this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
48492         
48493         var keyPress = function(e){
48494             
48495             var k = e.getKey();
48496             
48497             var c = e.getCharCode();
48498             
48499             if(
48500                     (String.fromCharCode(c) == '.' || String.fromCharCode(c) == '-') &&
48501                     allowed.indexOf(String.fromCharCode(c)) === -1
48502             ){
48503                 e.stopEvent();
48504                 return;
48505             }
48506             
48507             if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
48508                 return;
48509             }
48510             
48511             if(allowed.indexOf(String.fromCharCode(c)) === -1){
48512                 e.stopEvent();
48513             }
48514         };
48515         
48516         this.inputEl().on("keypress", keyPress, this);
48517         
48518     },
48519     
48520     onTriggerClick : function(e)
48521     {   
48522         if(this.disabled){
48523             return;
48524         }
48525         
48526         this.page = 0;
48527         this.loadNext = false;
48528         
48529         if(this.isExpanded()){
48530             this.collapse();
48531             return;
48532         }
48533         
48534         this.hasFocus = true;
48535         
48536         if(this.triggerAction == 'all') {
48537             this.doQuery(this.allQuery, true);
48538             return;
48539         }
48540         
48541         this.doQuery(this.getRawValue());
48542     },
48543     
48544     getCurrency : function()
48545     {   
48546         var v = this.currencyEl().getValue();
48547         
48548         return v;
48549     },
48550     
48551     restrictHeight : function()
48552     {
48553         this.list.alignTo(this.currencyEl(), this.listAlign);
48554         this.list.alignTo(this.currencyEl(), this.listAlign);
48555     },
48556     
48557     onViewClick : function(view, doFocus, el, e)
48558     {
48559         var index = this.view.getSelectedIndexes()[0];
48560         
48561         var r = this.store.getAt(index);
48562         
48563         if(r){
48564             this.onSelect(r, index);
48565         }
48566     },
48567     
48568     onSelect : function(record, index){
48569         
48570         if(this.fireEvent('beforeselect', this, record, index) !== false){
48571         
48572             this.setFromCurrencyData(index > -1 ? record.data : false);
48573             
48574             this.collapse();
48575             
48576             this.fireEvent('select', this, record, index);
48577         }
48578     },
48579     
48580     setFromCurrencyData : function(o)
48581     {
48582         var currency = '';
48583         
48584         this.lastCurrency = o;
48585         
48586         if (this.currencyField) {
48587             currency = !o || typeof(o[this.currencyField]) == 'undefined' ? '' : o[this.currencyField];
48588         } else {
48589             Roo.log('no  currencyField value set for '+ (this.name ? this.name : this.id));
48590         }
48591         
48592         this.lastSelectionText = currency;
48593         
48594         //setting default currency
48595         if(o[this.currencyField] * 1 == 0 && this.defaultCurrency) {
48596             this.setCurrency(this.defaultCurrency);
48597             return;
48598         }
48599         
48600         this.setCurrency(currency);
48601     },
48602     
48603     setFromData : function(o)
48604     {
48605         var c = {};
48606         
48607         c[this.currencyField] = !o || typeof(o[this.currencyName]) == 'undefined' ? '' : o[this.currencyName];
48608         
48609         this.setFromCurrencyData(c);
48610         
48611         var value = '';
48612         
48613         if (this.name) {
48614             value = !o || typeof(o[this.name]) == 'undefined' ? '' : o[this.name];
48615         } else {
48616             Roo.log('no value set for '+ (this.name ? this.name : this.id));
48617         }
48618         
48619         this.setValue(value);
48620         
48621     },
48622     
48623     setCurrency : function(v)
48624     {   
48625         this.currencyValue = v;
48626         
48627         if(this.rendered){
48628             this.currencyEl().dom.value = (v === null || v === undefined ? '' : v);
48629             this.validate();
48630         }
48631     },
48632     
48633     setValue : function(v)
48634     {
48635         v = String(this.fixPrecision(v)).replace(".", this.decimalSeparator);
48636         
48637         this.value = v;
48638         
48639         if(this.rendered){
48640             
48641             this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
48642             
48643             this.inputEl().dom.value = (v == '') ? '' :
48644                 Roo.util.Format.number(v, this.decimalPrecision, this.thousandsDelimiter || '');
48645             
48646             if(!this.allowZero && v === '0') {
48647                 this.hiddenEl().dom.value = '';
48648                 this.inputEl().dom.value = '';
48649             }
48650             
48651             this.validate();
48652         }
48653     },
48654     
48655     getRawValue : function()
48656     {
48657         var v = this.inputEl().getValue();
48658         
48659         return v;
48660     },
48661     
48662     getValue : function()
48663     {
48664         return this.fixPrecision(this.parseValue(this.getRawValue()));
48665     },
48666     
48667     parseValue : function(value)
48668     {
48669         if(this.thousandsDelimiter) {
48670             value += "";
48671             r = new RegExp(",", "g");
48672             value = value.replace(r, "");
48673         }
48674         
48675         value = parseFloat(String(value).replace(this.decimalSeparator, "."));
48676         return isNaN(value) ? '' : value;
48677         
48678     },
48679     
48680     fixPrecision : function(value)
48681     {
48682         if(this.thousandsDelimiter) {
48683             value += "";
48684             r = new RegExp(",", "g");
48685             value = value.replace(r, "");
48686         }
48687         
48688         var nan = isNaN(value);
48689         
48690         if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
48691             return nan ? '' : value;
48692         }
48693         return parseFloat(value).toFixed(this.decimalPrecision);
48694     },
48695     
48696     decimalPrecisionFcn : function(v)
48697     {
48698         return Math.floor(v);
48699     },
48700     
48701     validateValue : function(value)
48702     {
48703         if(!Roo.bootstrap.form.MoneyField.superclass.validateValue.call(this, value)){
48704             return false;
48705         }
48706         
48707         var num = this.parseValue(value);
48708         
48709         if(isNaN(num)){
48710             this.markInvalid(String.format(this.nanText, value));
48711             return false;
48712         }
48713         
48714         if(num < this.minValue){
48715             this.markInvalid(String.format(this.minText, this.minValue));
48716             return false;
48717         }
48718         
48719         if(num > this.maxValue){
48720             this.markInvalid(String.format(this.maxText, this.maxValue));
48721             return false;
48722         }
48723         
48724         return true;
48725     },
48726     
48727     validate : function()
48728     {
48729         if(this.disabled || this.allowBlank){
48730             this.markValid();
48731             return true;
48732         }
48733         
48734         var currency = this.getCurrency();
48735         
48736         if(this.validateValue(this.getRawValue()) && currency.length){
48737             this.markValid();
48738             return true;
48739         }
48740         
48741         this.markInvalid();
48742         return false;
48743     },
48744     
48745     getName: function()
48746     {
48747         return this.name;
48748     },
48749     
48750     beforeBlur : function()
48751     {
48752         if(!this.castInt){
48753             return;
48754         }
48755         
48756         var v = this.parseValue(this.getRawValue());
48757         
48758         if(v || v == 0){
48759             this.setValue(v);
48760         }
48761     },
48762     
48763     onBlur : function()
48764     {
48765         this.beforeBlur();
48766         
48767         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
48768             //this.el.removeClass(this.focusClass);
48769         }
48770         
48771         this.hasFocus = false;
48772         
48773         if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
48774             this.validate();
48775         }
48776         
48777         var v = this.getValue();
48778         
48779         if(String(v) !== String(this.startValue)){
48780             this.fireEvent('change', this, v, this.startValue);
48781         }
48782         
48783         this.fireEvent("blur", this);
48784     },
48785     
48786     inputEl : function()
48787     {
48788         return this.el.select('.roo-money-amount-input', true).first();
48789     },
48790     
48791     currencyEl : function()
48792     {
48793         return this.el.select('.roo-money-currency-input', true).first();
48794     },
48795     
48796     hiddenEl : function()
48797     {
48798         return this.el.select('input.hidden-number-input',true).first();
48799     }
48800     
48801 });/**
48802  * @class Roo.bootstrap.BezierSignature
48803  * @extends Roo.bootstrap.Component
48804  * Bootstrap BezierSignature class
48805  * This script refer to:
48806  *    Title: Signature Pad
48807  *    Author: szimek
48808  *    Availability: https://github.com/szimek/signature_pad
48809  *
48810  * @constructor
48811  * Create a new BezierSignature
48812  * @param {Object} config The config object
48813  */
48814
48815 Roo.bootstrap.BezierSignature = function(config){
48816     Roo.bootstrap.BezierSignature.superclass.constructor.call(this, config);
48817     this.addEvents({
48818         "resize" : true
48819     });
48820 };
48821
48822 Roo.extend(Roo.bootstrap.BezierSignature, Roo.bootstrap.Component,
48823 {
48824      
48825     curve_data: [],
48826     
48827     is_empty: true,
48828     
48829     mouse_btn_down: true,
48830     
48831     /**
48832      * @cfg {int} canvas height
48833      */
48834     canvas_height: '200px',
48835     
48836     /**
48837      * @cfg {float|function} Radius of a single dot.
48838      */ 
48839     dot_size: false,
48840     
48841     /**
48842      * @cfg {float} Minimum width of a line. Defaults to 0.5.
48843      */
48844     min_width: 0.5,
48845     
48846     /**
48847      * @cfg {float} Maximum width of a line. Defaults to 2.5.
48848      */
48849     max_width: 2.5,
48850     
48851     /**
48852      * @cfg {integer} Draw the next point at most once per every x milliseconds. Set it to 0 to turn off throttling. Defaults to 16.
48853      */
48854     throttle: 16,
48855     
48856     /**
48857      * @cfg {integer} Add the next point only if the previous one is farther than x pixels. Defaults to 5.
48858      */
48859     min_distance: 5,
48860     
48861     /**
48862      * @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.
48863      */
48864     bg_color: 'rgba(0, 0, 0, 0)',
48865     
48866     /**
48867      * @cfg {string} Color used to draw the lines. Can be any color format accepted by context.fillStyle. Defaults to "black".
48868      */
48869     dot_color: 'black',
48870     
48871     /**
48872      * @cfg {float} Weight used to modify new velocity based on the previous velocity. Defaults to 0.7.
48873      */ 
48874     velocity_filter_weight: 0.7,
48875     
48876     /**
48877      * @cfg {function} Callback when stroke begin. 
48878      */
48879     onBegin: false,
48880     
48881     /**
48882      * @cfg {function} Callback when stroke end.
48883      */
48884     onEnd: false,
48885     
48886     getAutoCreate : function()
48887     {
48888         var cls = 'roo-signature column';
48889         
48890         if(this.cls){
48891             cls += ' ' + this.cls;
48892         }
48893         
48894         var col_sizes = [
48895             'lg',
48896             'md',
48897             'sm',
48898             'xs'
48899         ];
48900         
48901         for(var i = 0; i < col_sizes.length; i++) {
48902             if(this[col_sizes[i]]) {
48903                 cls += " col-"+col_sizes[i]+"-"+this[col_sizes[i]];
48904             }
48905         }
48906         
48907         var cfg = {
48908             tag: 'div',
48909             cls: cls,
48910             cn: [
48911                 {
48912                     tag: 'div',
48913                     cls: 'roo-signature-body',
48914                     cn: [
48915                         {
48916                             tag: 'canvas',
48917                             cls: 'roo-signature-body-canvas',
48918                             height: this.canvas_height,
48919                             width: this.canvas_width
48920                         }
48921                     ]
48922                 },
48923                 {
48924                     tag: 'input',
48925                     type: 'file',
48926                     style: 'display: none'
48927                 }
48928             ]
48929         };
48930         
48931         return cfg;
48932     },
48933     
48934     initEvents: function() 
48935     {
48936         Roo.bootstrap.BezierSignature.superclass.initEvents.call(this);
48937         
48938         var canvas = this.canvasEl();
48939         
48940         // mouse && touch event swapping...
48941         canvas.dom.style.touchAction = 'none';
48942         canvas.dom.style.msTouchAction = 'none';
48943         
48944         this.mouse_btn_down = false;
48945         canvas.on('mousedown', this._handleMouseDown, this);
48946         canvas.on('mousemove', this._handleMouseMove, this);
48947         Roo.select('html').first().on('mouseup', this._handleMouseUp, this);
48948         
48949         if (window.PointerEvent) {
48950             canvas.on('pointerdown', this._handleMouseDown, this);
48951             canvas.on('pointermove', this._handleMouseMove, this);
48952             Roo.select('html').first().on('pointerup', this._handleMouseUp, this);
48953         }
48954         
48955         if ('ontouchstart' in window) {
48956             canvas.on('touchstart', this._handleTouchStart, this);
48957             canvas.on('touchmove', this._handleTouchMove, this);
48958             canvas.on('touchend', this._handleTouchEnd, this);
48959         }
48960         
48961         Roo.EventManager.onWindowResize(this.resize, this, true);
48962         
48963         // file input event
48964         this.fileEl().on('change', this.uploadImage, this);
48965         
48966         this.clear();
48967         
48968         this.resize();
48969     },
48970     
48971     resize: function(){
48972         
48973         var canvas = this.canvasEl().dom;
48974         var ctx = this.canvasElCtx();
48975         var img_data = false;
48976         
48977         if(canvas.width > 0) {
48978             var img_data = ctx.getImageData(0, 0, canvas.width, canvas.height);
48979         }
48980         // setting canvas width will clean img data
48981         canvas.width = 0;
48982         
48983         var style = window.getComputedStyle ? 
48984             getComputedStyle(this.el.dom, null) : this.el.dom.currentStyle;
48985             
48986         var padding_left = parseInt(style.paddingLeft) || 0;
48987         var padding_right = parseInt(style.paddingRight) || 0;
48988         
48989         canvas.width = this.el.dom.clientWidth - padding_left - padding_right;
48990         
48991         if(img_data) {
48992             ctx.putImageData(img_data, 0, 0);
48993         }
48994     },
48995     
48996     _handleMouseDown: function(e)
48997     {
48998         if (e.browserEvent.which === 1) {
48999             this.mouse_btn_down = true;
49000             this.strokeBegin(e);
49001         }
49002     },
49003     
49004     _handleMouseMove: function (e)
49005     {
49006         if (this.mouse_btn_down) {
49007             this.strokeMoveUpdate(e);
49008         }
49009     },
49010     
49011     _handleMouseUp: function (e)
49012     {
49013         if (e.browserEvent.which === 1 && this.mouse_btn_down) {
49014             this.mouse_btn_down = false;
49015             this.strokeEnd(e);
49016         }
49017     },
49018     
49019     _handleTouchStart: function (e) {
49020         
49021         e.preventDefault();
49022         if (e.browserEvent.targetTouches.length === 1) {
49023             // var touch = e.browserEvent.changedTouches[0];
49024             // this.strokeBegin(touch);
49025             
49026              this.strokeBegin(e); // assume e catching the correct xy...
49027         }
49028     },
49029     
49030     _handleTouchMove: function (e) {
49031         e.preventDefault();
49032         // var touch = event.targetTouches[0];
49033         // _this._strokeMoveUpdate(touch);
49034         this.strokeMoveUpdate(e);
49035     },
49036     
49037     _handleTouchEnd: function (e) {
49038         var wasCanvasTouched = e.target === this.canvasEl().dom;
49039         if (wasCanvasTouched) {
49040             e.preventDefault();
49041             // var touch = event.changedTouches[0];
49042             // _this._strokeEnd(touch);
49043             this.strokeEnd(e);
49044         }
49045     },
49046     
49047     reset: function () {
49048         this._lastPoints = [];
49049         this._lastVelocity = 0;
49050         this._lastWidth = (this.min_width + this.max_width) / 2;
49051         this.canvasElCtx().fillStyle = this.dot_color;
49052     },
49053     
49054     strokeMoveUpdate: function(e)
49055     {
49056         this.strokeUpdate(e);
49057         
49058         if (this.throttle) {
49059             this.throttleStroke(this.strokeUpdate, this.throttle);
49060         }
49061         else {
49062             this.strokeUpdate(e);
49063         }
49064     },
49065     
49066     strokeBegin: function(e)
49067     {
49068         var newPointGroup = {
49069             color: this.dot_color,
49070             points: []
49071         };
49072         
49073         if (typeof this.onBegin === 'function') {
49074             this.onBegin(e);
49075         }
49076         
49077         this.curve_data.push(newPointGroup);
49078         this.reset();
49079         this.strokeUpdate(e);
49080     },
49081     
49082     strokeUpdate: function(e)
49083     {
49084         var rect = this.canvasEl().dom.getBoundingClientRect();
49085         var point = new this.Point(e.xy[0] - rect.left, e.xy[1] - rect.top, new Date().getTime());
49086         var lastPointGroup = this.curve_data[this.curve_data.length - 1];
49087         var lastPoints = lastPointGroup.points;
49088         var lastPoint = lastPoints.length > 0 && lastPoints[lastPoints.length - 1];
49089         var isLastPointTooClose = lastPoint
49090             ? point.distanceTo(lastPoint) <= this.min_distance
49091             : false;
49092         var color = lastPointGroup.color;
49093         if (!lastPoint || !(lastPoint && isLastPointTooClose)) {
49094             var curve = this.addPoint(point);
49095             if (!lastPoint) {
49096                 this.drawDot({color: color, point: point});
49097             }
49098             else if (curve) {
49099                 this.drawCurve({color: color, curve: curve});
49100             }
49101             lastPoints.push({
49102                 time: point.time,
49103                 x: point.x,
49104                 y: point.y
49105             });
49106         }
49107     },
49108     
49109     strokeEnd: function(e)
49110     {
49111         this.strokeUpdate(e);
49112         if (typeof this.onEnd === 'function') {
49113             this.onEnd(e);
49114         }
49115     },
49116     
49117     addPoint:  function (point) {
49118         var _lastPoints = this._lastPoints;
49119         _lastPoints.push(point);
49120         if (_lastPoints.length > 2) {
49121             if (_lastPoints.length === 3) {
49122                 _lastPoints.unshift(_lastPoints[0]);
49123             }
49124             var widths = this.calculateCurveWidths(_lastPoints[1], _lastPoints[2]);
49125             var curve = this.Bezier.fromPoints(_lastPoints, widths, this);
49126             _lastPoints.shift();
49127             return curve;
49128         }
49129         return null;
49130     },
49131     
49132     calculateCurveWidths: function (startPoint, endPoint) {
49133         var velocity = this.velocity_filter_weight * endPoint.velocityFrom(startPoint) +
49134             (1 - this.velocity_filter_weight) * this._lastVelocity;
49135
49136         var newWidth = Math.max(this.max_width / (velocity + 1), this.min_width);
49137         var widths = {
49138             end: newWidth,
49139             start: this._lastWidth
49140         };
49141         
49142         this._lastVelocity = velocity;
49143         this._lastWidth = newWidth;
49144         return widths;
49145     },
49146     
49147     drawDot: function (_a) {
49148         var color = _a.color, point = _a.point;
49149         var ctx = this.canvasElCtx();
49150         var width = typeof this.dot_size === 'function' ? this.dot_size() : this.dot_size;
49151         ctx.beginPath();
49152         this.drawCurveSegment(point.x, point.y, width);
49153         ctx.closePath();
49154         ctx.fillStyle = color;
49155         ctx.fill();
49156     },
49157     
49158     drawCurve: function (_a) {
49159         var color = _a.color, curve = _a.curve;
49160         var ctx = this.canvasElCtx();
49161         var widthDelta = curve.endWidth - curve.startWidth;
49162         var drawSteps = Math.floor(curve.length()) * 2;
49163         ctx.beginPath();
49164         ctx.fillStyle = color;
49165         for (var i = 0; i < drawSteps; i += 1) {
49166         var t = i / drawSteps;
49167         var tt = t * t;
49168         var ttt = tt * t;
49169         var u = 1 - t;
49170         var uu = u * u;
49171         var uuu = uu * u;
49172         var x = uuu * curve.startPoint.x;
49173         x += 3 * uu * t * curve.control1.x;
49174         x += 3 * u * tt * curve.control2.x;
49175         x += ttt * curve.endPoint.x;
49176         var y = uuu * curve.startPoint.y;
49177         y += 3 * uu * t * curve.control1.y;
49178         y += 3 * u * tt * curve.control2.y;
49179         y += ttt * curve.endPoint.y;
49180         var width = curve.startWidth + ttt * widthDelta;
49181         this.drawCurveSegment(x, y, width);
49182         }
49183         ctx.closePath();
49184         ctx.fill();
49185     },
49186     
49187     drawCurveSegment: function (x, y, width) {
49188         var ctx = this.canvasElCtx();
49189         ctx.moveTo(x, y);
49190         ctx.arc(x, y, width, 0, 2 * Math.PI, false);
49191         this.is_empty = false;
49192     },
49193     
49194     clear: function()
49195     {
49196         var ctx = this.canvasElCtx();
49197         var canvas = this.canvasEl().dom;
49198         ctx.fillStyle = this.bg_color;
49199         ctx.clearRect(0, 0, canvas.width, canvas.height);
49200         ctx.fillRect(0, 0, canvas.width, canvas.height);
49201         this.curve_data = [];
49202         this.reset();
49203         this.is_empty = true;
49204     },
49205     
49206     fileEl: function()
49207     {
49208         return  this.el.select('input',true).first();
49209     },
49210     
49211     canvasEl: function()
49212     {
49213         return this.el.select('canvas',true).first();
49214     },
49215     
49216     canvasElCtx: function()
49217     {
49218         return this.el.select('canvas',true).first().dom.getContext('2d');
49219     },
49220     
49221     getImage: function(type)
49222     {
49223         if(this.is_empty) {
49224             return false;
49225         }
49226         
49227         // encryption ?
49228         return this.canvasEl().dom.toDataURL('image/'+type, 1);
49229     },
49230     
49231     drawFromImage: function(img_src)
49232     {
49233         var img = new Image();
49234         
49235         img.onload = function(){
49236             this.canvasElCtx().drawImage(img, 0, 0);
49237         }.bind(this);
49238         
49239         img.src = img_src;
49240         
49241         this.is_empty = false;
49242     },
49243     
49244     selectImage: function()
49245     {
49246         this.fileEl().dom.click();
49247     },
49248     
49249     uploadImage: function(e)
49250     {
49251         var reader = new FileReader();
49252         
49253         reader.onload = function(e){
49254             var img = new Image();
49255             img.onload = function(){
49256                 this.reset();
49257                 this.canvasElCtx().drawImage(img, 0, 0);
49258             }.bind(this);
49259             img.src = e.target.result;
49260         }.bind(this);
49261         
49262         reader.readAsDataURL(e.target.files[0]);
49263     },
49264     
49265     // Bezier Point Constructor
49266     Point: (function () {
49267         function Point(x, y, time) {
49268             this.x = x;
49269             this.y = y;
49270             this.time = time || Date.now();
49271         }
49272         Point.prototype.distanceTo = function (start) {
49273             return Math.sqrt(Math.pow(this.x - start.x, 2) + Math.pow(this.y - start.y, 2));
49274         };
49275         Point.prototype.equals = function (other) {
49276             return this.x === other.x && this.y === other.y && this.time === other.time;
49277         };
49278         Point.prototype.velocityFrom = function (start) {
49279             return this.time !== start.time
49280             ? this.distanceTo(start) / (this.time - start.time)
49281             : 0;
49282         };
49283         return Point;
49284     }()),
49285     
49286     
49287     // Bezier Constructor
49288     Bezier: (function () {
49289         function Bezier(startPoint, control2, control1, endPoint, startWidth, endWidth) {
49290             this.startPoint = startPoint;
49291             this.control2 = control2;
49292             this.control1 = control1;
49293             this.endPoint = endPoint;
49294             this.startWidth = startWidth;
49295             this.endWidth = endWidth;
49296         }
49297         Bezier.fromPoints = function (points, widths, scope) {
49298             var c2 = this.calculateControlPoints(points[0], points[1], points[2], scope).c2;
49299             var c3 = this.calculateControlPoints(points[1], points[2], points[3], scope).c1;
49300             return new Bezier(points[1], c2, c3, points[2], widths.start, widths.end);
49301         };
49302         Bezier.calculateControlPoints = function (s1, s2, s3, scope) {
49303             var dx1 = s1.x - s2.x;
49304             var dy1 = s1.y - s2.y;
49305             var dx2 = s2.x - s3.x;
49306             var dy2 = s2.y - s3.y;
49307             var m1 = { x: (s1.x + s2.x) / 2.0, y: (s1.y + s2.y) / 2.0 };
49308             var m2 = { x: (s2.x + s3.x) / 2.0, y: (s2.y + s3.y) / 2.0 };
49309             var l1 = Math.sqrt(dx1 * dx1 + dy1 * dy1);
49310             var l2 = Math.sqrt(dx2 * dx2 + dy2 * dy2);
49311             var dxm = m1.x - m2.x;
49312             var dym = m1.y - m2.y;
49313             var k = l2 / (l1 + l2);
49314             var cm = { x: m2.x + dxm * k, y: m2.y + dym * k };
49315             var tx = s2.x - cm.x;
49316             var ty = s2.y - cm.y;
49317             return {
49318                 c1: new scope.Point(m1.x + tx, m1.y + ty),
49319                 c2: new scope.Point(m2.x + tx, m2.y + ty)
49320             };
49321         };
49322         Bezier.prototype.length = function () {
49323             var steps = 10;
49324             var length = 0;
49325             var px;
49326             var py;
49327             for (var i = 0; i <= steps; i += 1) {
49328                 var t = i / steps;
49329                 var cx = this.point(t, this.startPoint.x, this.control1.x, this.control2.x, this.endPoint.x);
49330                 var cy = this.point(t, this.startPoint.y, this.control1.y, this.control2.y, this.endPoint.y);
49331                 if (i > 0) {
49332                     var xdiff = cx - px;
49333                     var ydiff = cy - py;
49334                     length += Math.sqrt(xdiff * xdiff + ydiff * ydiff);
49335                 }
49336                 px = cx;
49337                 py = cy;
49338             }
49339             return length;
49340         };
49341         Bezier.prototype.point = function (t, start, c1, c2, end) {
49342             return (start * (1.0 - t) * (1.0 - t) * (1.0 - t))
49343             + (3.0 * c1 * (1.0 - t) * (1.0 - t) * t)
49344             + (3.0 * c2 * (1.0 - t) * t * t)
49345             + (end * t * t * t);
49346         };
49347         return Bezier;
49348     }()),
49349     
49350     throttleStroke: function(fn, wait) {
49351       if (wait === void 0) { wait = 250; }
49352       var previous = 0;
49353       var timeout = null;
49354       var result;
49355       var storedContext;
49356       var storedArgs;
49357       var later = function () {
49358           previous = Date.now();
49359           timeout = null;
49360           result = fn.apply(storedContext, storedArgs);
49361           if (!timeout) {
49362               storedContext = null;
49363               storedArgs = [];
49364           }
49365       };
49366       return function wrapper() {
49367           var args = [];
49368           for (var _i = 0; _i < arguments.length; _i++) {
49369               args[_i] = arguments[_i];
49370           }
49371           var now = Date.now();
49372           var remaining = wait - (now - previous);
49373           storedContext = this;
49374           storedArgs = args;
49375           if (remaining <= 0 || remaining > wait) {
49376               if (timeout) {
49377                   clearTimeout(timeout);
49378                   timeout = null;
49379               }
49380               previous = now;
49381               result = fn.apply(storedContext, storedArgs);
49382               if (!timeout) {
49383                   storedContext = null;
49384                   storedArgs = [];
49385               }
49386           }
49387           else if (!timeout) {
49388               timeout = window.setTimeout(later, remaining);
49389           }
49390           return result;
49391       };
49392   }
49393   
49394 });
49395
49396  
49397
49398  // old names for form elements
49399 Roo.bootstrap.Form          =   Roo.bootstrap.form.Form;
49400 Roo.bootstrap.Input         =   Roo.bootstrap.form.Input;
49401 Roo.bootstrap.TextArea      =   Roo.bootstrap.form.TextArea;
49402 Roo.bootstrap.TriggerField  =   Roo.bootstrap.form.TriggerField;
49403 Roo.bootstrap.ComboBox      =   Roo.bootstrap.form.ComboBox;
49404 Roo.bootstrap.DateField     =   Roo.bootstrap.form.DateField;
49405 Roo.bootstrap.TimeField     =   Roo.bootstrap.form.TimeField;
49406 Roo.bootstrap.MonthField    =   Roo.bootstrap.form.MonthField;
49407 Roo.bootstrap.CheckBox      =   Roo.bootstrap.form.CheckBox;
49408 Roo.bootstrap.Radio         =   Roo.bootstrap.form.Radio;
49409 Roo.bootstrap.RadioSet      =   Roo.bootstrap.form.RadioSet;
49410 Roo.bootstrap.SecurePass    =   Roo.bootstrap.form.SecurePass;
49411 Roo.bootstrap.FieldLabel    =   Roo.bootstrap.form.FieldLabel;
49412 Roo.bootstrap.DateSplitField=   Roo.bootstrap.form.DateSplitField;
49413 Roo.bootstrap.NumberField   =   Roo.bootstrap.form.NumberField;
49414 Roo.bootstrap.PhoneInput    =   Roo.bootstrap.form.PhoneInput;
49415 Roo.bootstrap.PhoneInputData=   Roo.bootstrap.form.PhoneInputData;
49416 Roo.bootstrap.MoneyField    =   Roo.bootstrap.form.MoneyField;
49417 Roo.bootstrap.HtmlEditor    =   Roo.bootstrap.form.HtmlEditor;
49418 Roo.bootstrap.HtmlEditor.ToolbarStandard =   Roo.bootstrap.form.HtmlEditorToolbarStandard;
49419 Roo.bootstrap.Markdown      = Roo.bootstrap.form.Markdown;
49420 Roo.bootstrap.CardUploader  = Roo.bootstrap.form.CardUploader;// depricated.
49421 Roo.bootstrap.Navbar            = Roo.bootstrap.nav.Bar;
49422 Roo.bootstrap.NavGroup          = Roo.bootstrap.nav.Group;
49423 Roo.bootstrap.NavHeaderbar      = Roo.bootstrap.nav.Headerbar;
49424 Roo.bootstrap.NavItem           = Roo.bootstrap.nav.Item;
49425
49426 Roo.bootstrap.NavProgressBar     = Roo.bootstrap.nav.ProgressBar;
49427 Roo.bootstrap.NavProgressBarItem = Roo.bootstrap.nav.ProgressBarItem;
49428
49429 Roo.bootstrap.NavSidebar        = Roo.bootstrap.nav.Sidebar;
49430 Roo.bootstrap.NavSidebarItem    = Roo.bootstrap.nav.SidebarItem;
49431
49432 Roo.bootstrap.NavSimplebar      = Roo.bootstrap.nav.Simplebar;// deprciated 
49433 Roo.bootstrap.Menu = Roo.bootstrap.menu.Menu;
49434 Roo.bootstrap.MenuItem =  Roo.bootstrap.menu.Item;
49435 Roo.bootstrap.MenuSeparator = Roo.bootstrap.menu.Separator
49436